Java Thread
2016-06-14 14:02
393 查看
1,线程的基本概念
线程是一个程序内部的顺序控制流。
线程和进程的区别:(资源分配和处理器分配的基本单元)
Java线程是通过java.lang.Thread类来实现的。VM启动时会有一个由主方法(public static void main(){})所定义的线程。可以通过创建Thread的实例来创建新的线程。每个线程都是通过某个特定的Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。
注意:
要注意方法调用和运行线程的区别。方法调用,主程序会等方法运行完成程序再继续往下走;运行线程:相当于新建一个分支,主程序与线程并行执行。
2,线程的创建和启动
第一种:定义线程类实现Runnable接口
Thread thread = new Thread(target);//target为Runnable接口类型
Runnable中只有一个方法:
public void run();用以定义线程运行体。
使用Runnable接口可以为多个线程提供共享的数据。在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法:
public static Thread currentThread()获取当前线程的引用。
例如:
第二种:定义一个Thread的子类,并重写其run方法,如:
class MyThread extends Thread {
public void run() {...}
}
然后生成该类的对象:MyThread thread = new MyThread(...);
例如:
3,线程的状态
3.1,线程的状态类型:
3.2,线程状态转换图
3.3,阻塞和挂起的区别:
3.4,wait和sleep 的区别
3.5,线程控制的基本方法
3.5.1,线程优先级
Java提供一个线程调度器来监控程序中就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。线程的优先级用数字标志,范围1~10,缺省优先级为5
3.5.2,sleep/join/yield方法
(1),sleep:可以调用Thread的静态方法sleep使得当前线程休眠,由于是静态方法,sleep可以直接由类名直接调用。
例子:
结果:
源码:
(2),join:合并线程
结果:
源码:
(3),yield:让出CPU,给其他线程执行的机会
例子:
结果:
源码:
4,线程同步
在Java语言中,引入了对象互斥锁的概念,保证共享数据错做的完整性,每个对象对应于一个可称为”互斥锁“的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。
关键字synchronized来与对象的互斥锁联系。当某个对象synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
例一:
结果一(可能):
加上synchronized控制
结果二:
例二:
结果:
例三:
哲学家吃饭问题
5,生产者消费者问题
结果:
线程是一个程序内部的顺序控制流。
线程和进程的区别:(资源分配和处理器分配的基本单元)
①,每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销; ②,线程可以看成是轻量级进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小; ③,多进程:操作系统中能同时运行多个任务(程序); ④,多线程:在同一应用程序中有多个顺序流同时执行;
Java线程是通过java.lang.Thread类来实现的。VM启动时会有一个由主方法(public static void main(){})所定义的线程。可以通过创建Thread的实例来创建新的线程。每个线程都是通过某个特定的Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。
注意:
要注意方法调用和运行线程的区别。方法调用,主程序会等方法运行完成程序再继续往下走;运行线程:相当于新建一个分支,主程序与线程并行执行。
2,线程的创建和启动
第一种:定义线程类实现Runnable接口
Thread thread = new Thread(target);//target为Runnable接口类型
Runnable中只有一个方法:
public void run();用以定义线程运行体。
使用Runnable接口可以为多个线程提供共享的数据。在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法:
public static Thread currentThread()获取当前线程的引用。
例如:
public static void main(String[] args) { Thread t = new Thread(new MyThread()); t.start();//线程启动 t.run();//方法调用 } class MyThread implements Runnable { public void run() { ... } }
第二种:定义一个Thread的子类,并重写其run方法,如:
class MyThread extends Thread {
public void run() {...}
}
然后生成该类的对象:MyThread thread = new MyThread(...);
例如:
public static void main(String[] args) { Thread t = new MyThread(); t.start(); } class MyThread extends Thread { public void run() { ... } }
3,线程的状态
3.1,线程的状态类型:
①,新建状态(New):创建了一个线程对象,它仅仅作为一个对象实例存在,JVM没有为其分配CPU时间片等线程运行资源; ②,就绪状态(Runnable):在处于创建状态的线程中调用start方法将线程的状态转换为就绪状态。这时,线程已经得到除CPU时间之外的其它系统资源,只等JVM的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU时间片的机会; ③,运行状态(Running):就绪状态的线程获取了CPU,执行程序代码; ④,阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种: (1),等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池(waitting queue)中; (2),同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中; (3),其他阻塞:运行的线程执行sleep()或join()方法,或者发出I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超市、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态; ⑤,挂起状态(Suspend):可以通过调用suspend方法(已过时)将线程的状态转换为挂起状态。这时,线程将释放占用的所有资源,由JVM调度转入临时存储空间,直至应用程序调用resume方法(已过时)恢复线程运行; ⑥,死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
3.2,线程状态转换图
3.3,阻塞和挂起的区别:
操作系统中挂起和阻塞的区别如下: (1),挂起是一种主动行为,因此恢复也应该要主动完成,而阻塞则是一种被动行为,是在等待事件或资源时任务的表现,你不知道他什么时候被阻塞(pend),也就不能确切 的知道他什么时候恢复阻塞。而且挂起队列在操作系统里可以看成一个,而阻塞队列则是不同的事件或资源(如信号量)就有自己的队列; (2),阻塞(pend)就是任务释放CPU,其他任务可以运行,一般在等待某种资源或信号量的时候出现。挂起(suspend)不释放CPU,如果任务优先级高就永远轮不到其他任务运行,一般挂起用于程序调试中的条件中断,当出现某个条件的情况下挂起,然后进行单步调试; (3),阻塞(pend)是task主动去等一个事件或消息,suspend是直接悬挂task,以后这个task和你没任何关系,任何task间的通信或者同步都和这个suspended task没任何关系了,除非你resume task; (4),任务调度是操作系统来实现的,任务调度时,直接忽略挂起状态的任务,但是会顾及处于pend下的任务,当pend下的任务等待的资源就绪后,就可以转为ready了。ready只需要等待CPU时间,当然,任务调度也占用开销,但是不大,可以忽略。可以这样理解,只要是挂起状态,操作系统就不在管理这个任务了; (5),挂起是主动的,一般需要用挂起函数进行操作,若没有resume的动作,则此任务一直不会ready。而阻塞是因为资源被其他任务抢占而处于休眠态。两者的表现方式都是从就绪态里“清掉”,即对应标志位清零,只不过实现方式不一样。
3.4,wait和sleep 的区别
(1),这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。 (2),最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。Thread.Sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。 (3),使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用 synchronized(x){ x.notify() //或者wait() } (4),sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
3.5,线程控制的基本方法
isAlive() | 判断线程是否还“活着”,即线程是否还未终止 |
getPriority() | 获得线程的优先级数值 |
setPriority(int newPriority) | 设置线程的优先级数值 |
sleep(long millis) sleep(long millis, int nanos) | 将当前线程睡眠指定时间 |
join() | 调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行 |
yield() | 让出CPU,当前线程进入就绪队列等待调度 |
wait() | 当前线程进入线程等待池(wait pool) |
notify() notifyAll() | 唤醒对象的wait pool中的一个/所有等待线程 |
Java提供一个线程调度器来监控程序中就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。线程的优先级用数字标志,范围1~10,缺省优先级为5
public static final int MIN_PRIORITY = 1; public static final int NORM_PRIORITY = 5; public static final int MAX_PRIORITY = 10; 使用以下方法获得或这只线程对象的优先级: public final void setPriority(int paramInt) { checkAccess(); if ((paramInt > 10) || (paramInt < 1)) { throw new IllegalArgumentException(); } ThreadGroup localThreadGroup; if ((localThreadGroup = getThreadGroup()) != null) { if (paramInt > localThreadGroup.getMaxPriority()) { paramInt = localThreadGroup.getMaxPriority(); } setPriority0(this.priority = paramInt); } } public final int getPriority() { return this.priority; }
3.5.2,sleep/join/yield方法
(1),sleep:可以调用Thread的静态方法sleep使得当前线程休眠,由于是静态方法,sleep可以直接由类名直接调用。
例子:
public class Test { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); try { Thread.sleep(10000);//主线程睡眠 } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt();//打断子线程(不推荐的方法,还有stop()) //thread.flag = false; } } class MyThread extends Thread { boolean flag = true; public void run() { while(flag) { System.out.println("====" + new Date() + "===="); try { sleep(1000); } catch (InterruptedException e) { return;//sleep时出现异常则线程直接结束 } } } }
结果:
====Tue Jun 14 11:05:30 CST 2016==== ====Tue Jun 14 11:05:31 CST 2016==== ====Tue Jun 14 11:05:32 CST 2016==== ====Tue Jun 14 11:05:33 CST 2016==== ====Tue Jun 14 11:05:34 CST 2016==== ====Tue Jun 14 11:05:35 CST 2016==== ====Tue Jun 14 11:05:36 CST 2016==== ====Tue Jun 14 11:05:37 CST 2016==== ====Tue Jun 14 11:05:38 CST 2016==== ====Tue Jun 14 11:05:39 CST 2016====
源码:
public static native void sleep(long paramLong) throws InterruptedException; public static void sleep(long paramLong, int paramInt) throws InterruptedException { if (paramLong < 0L) { throw new IllegalArgumentException("timeout value is negative"); } if ((paramInt < 0) || (paramInt > 999999)) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if ((paramInt >= 500000) || ((paramInt != 0) && (paramLong == 0L))) { paramLong += 1L; } sleep(paramLong); }
(2),join:合并线程
public class Test { public static void main(String[] args) { MyThread thread = new MyThread("abcde"); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 3; i++) { System.out.println("I am main thread"); } } } class MyThread extends Thread { MyThread(String name) { super(name); } public void run() { for (int i = 0; i < 3; i++) { System.out.println("I am " + getName()); } } }
结果:
I am abcde I am abcde I am abcde I am main thread I am main thread I am main thread
源码:
public final void join() throws InterruptedException { join(0L); } public final synchronized void join(long paramLong) throws InterruptedException { long l1 = System.currentTimeMillis(); long l2 = 0L; if (paramLong < 0L) { throw new IllegalArgumentException("timeout value is negative"); } if (paramLong == 0L) { while (isAlive()) { wait(0L); } } while (isAlive()) { long l3 = paramLong - l2; if (l3 <= 0L) { break; } wait(l3); l2 = System.currentTimeMillis() - l1; } }
(3),yield:让出CPU,给其他线程执行的机会
例子:
public class Test { public static void main(String[] args) { MyThread t1 = new MyThread("t1"); MyThread t2 = new MyThread("t2"); t1.start(); t2.start(); } } class MyThread extends Thread { MyThread(String name) { super(name); } public void run() { for (int i = 1; i <= 100; i++) { System.out.println(getName() + ":" + i); if(i%10 == 0) { yield(); } } } }
结果:
每当被10整除的时候会yield ... t1:14 t2:70 t1:15 ... ... t2:72 t1:20 t2:73 ... t2:76 t1:30 t2:77 ...
源码:
public static native void yield();
4,线程同步
在Java语言中,引入了对象互斥锁的概念,保证共享数据错做的完整性,每个对象对应于一个可称为”互斥锁“的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。
关键字synchronized来与对象的互斥锁联系。当某个对象synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
例一:
public class Test implements Runnable { Timer timer = new Timer(); public static void main(String[] args) { Test test = new Test(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start(); } @Override public void run() { timer.add(Thread.currentThread().getName()); } } class Timer { private static int num = 0; public synchronized void add(String name) { // synchronized (this) { num++; try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(name + ", 你是第" + num + "个使用timer的线程"); // } } }
结果一(可能):
t1, 你是第2个使用timer的线程 t2, 你是第2个使用timer的线程
加上synchronized控制
结果二:
t1, 你是第1个使用timer的线程 t2, 你是第2个使用timer的线程
例二:
public class Test implements Runnable { public int flag = 1; static Object o1 = new Object(); static Object o2 = new Object(); @Override public void run() { System.out.println("flag = " + flag); if(flag == 1) { synchronized (o1) {//获得对象o1锁 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2) {//获得对象o2锁 System.out.println(1); } } } if(flag == 0) { synchronized (o2) {//获得对象o2锁 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1) {//获得对象o1锁 System.out.println(0); } } } } public static void main(String[] args) { Test test1 = new Test(); Test test2 = new Test(); test1.flag = 1; test2.flag = 0; Thread t1 = new Thread(test1); Thread t2 = new Thread(test2); t1.start(); t2.start(); } }
结果:
flag = 0 flag = 1 分别在等待获得o1,o2对象锁的,程序死锁,无法结束
例三:
哲学家吃饭问题
5,生产者消费者问题
public class Test { public static void main(String[] args) { SyncStack ss = new SyncStack(); Producer p = new Producer(ss); Consumer c = new Consumer(ss); new Thread(c).start(); new Thread(p).start(); new Thread(p).start();//多加个生产者线程 } } class WoTou { int id; WoTou(int id) { this.id = id; } public String toString() { return "WoTou:" + id; } } class SyncStack { int index = 0; WoTou[] arrWT = new WoTou[5]; //push public synchronized void push(WoTou wt) { while(index == arrWT.length) { try { this.wait();//阻塞 } catch (InterruptedException e) { e.printStackTrace(); } } this.notifyAll();//唤醒其他所有线程 arrWT[index] = wt; index++; } //pop public synchronized WoTou pop() { while (index == 0) { try { this.wait();//阻塞 } catch (InterruptedException e) { e.printStackTrace(); } } this.notifyAll();//唤醒其他所有线程 index--; return arrWT[index]; } } //生产者 class Producer implements Runnable { SyncStack ss = null; Producer(SyncStack ss) { this.ss = ss; } @Override public void run() { for(int i=1; i<= 10; i++) { WoTou wt = new WoTou(i);//produce ss.push(wt); System.out.println("生产了:" + wt); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } //消费者 class Consumer implements Runnable { SyncStack ss = null; Consumer(SyncStack ss) { this.ss = ss; } @Override public void run() { for (int i = 1; i <= 10; i++) { WoTou wt = ss.pop(); System.out.println("消费了:" + wt); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
结果:
生产了:WoTou:1 消费了:WoTou:1 生产了:WoTou:1 生产了:WoTou:2 消费了:WoTou:2 生产了:WoTou:2 消费了:WoTou:2 生产了:WoTou:3 生产了:WoTou:3 生产了:WoTou:4 生产了:WoTou:4 消费了:WoTou:4 生产了:WoTou:5 消费了:WoTou:5 生产了:WoTou:5 生产了:WoTou:6 消费了:WoTou:5 消费了:WoTou:6 生产了:WoTou:6 生产了:WoTou:7 消费了:WoTou:6 消费了:WoTou:7 生产了:WoTou:8 生产了:WoTou:9 消费了:WoTou:8
相关文章推荐
- 下载文件打包 zip java
- java 整型转字节类型
- JHTP练习题及课题_第二章_Java应用、输入、输出及运算符简介
- spring mvc ajax jsonp 跨域请求 返回值 配置
- spring事务传播深入解析
- java Collection
- Java编程开发设计模式之--装饰者模式(Decorator)与代理模式(Proxy)
- Struts2、SpringMVC、Servlet(Jsp)性能对比 测试
- Spring 的面向切面编程(AOP)
- JHTP自测题_第二章_Java应用、输入、输出及运算符简介
- 深入理解java String类
- spring 整合 mybatis
- 使用java流在文件末尾追加文本
- Ubuntu12编译openjdk7
- Factorial Trailing Zeroes - Javacript
- 【java线程池】ThreadPoolExecutor详解
- eclipse进行debug时进入threadpoolexecutor.class中的解决办法
- java利用io流读取txt文件
- SSM框架
- Java SE 中的泛型知道多少呢