Java线程状态及 wait、sleep、join、interrupt、yield等的区别
Java中的线程状态(详见Java线程状态及转换-MarchOn):
- wait:Object类的实例方法,释放CPU执行权,进入等待状态,直到 被中断、被拥有该对象锁的线程唤醒(notify或notifyAll)、wait时间到了自己唤醒 三者之一发生。会释放所持有的对象锁。(关于 wait、notiy、notifyAll的配合原理见后面)
示例:wait()和notify()因为会对对象的“锁标志”进行操作,所以它们必须在获得对象锁后执行即在 synchronized函数或synchronized block中进行调用,否则如果虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。
1 package buaa.act.ucar.imtg.main; 2 3 /** 4 * @author zsm 5 * @date 2017年3月3日 上午10:23:53 6 */ 7 public class Test extends Thread { 8 public static void main(String[] args) { 9 // TODO Auto-generated method stub 10 Test test = new Test(); 11 test.start(); 12 try { 13 synchronized (test) { 14 Thread.sleep(1000); 15 System.out.println("wake the thread in main..."); 16 test.isFire = true; 17 test.notify(); 18 } 19 20 } catch (InterruptedException e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 } 24 } 25 26 /** 27 * 线程锁 28 */ 29 private final Object object = new Object(); 30 31 private volatile boolean isFire = false; 32 33 @Override 34 public void run() { 35 System.out.println("开始执行线程。。。"); 36 System.out.println("进入等待状态。。。"); 37 synchronized (this) { 38 try { 39 while (!isFire) { 40 wait(); 41 } 42 } catch (InterruptedException e) { 43 e.printStackTrace(); 44 } 45 } 46 System.out.println("线程结束。。。"); 47 } 48 }
- join:Thread实例的方法,释放CPU执行权,等待被调用join方法的线程结束才继续执行本线程下面的操作。内部是在循环调用wait方法。
- sleep:Thread类的静态方法,释放CPU执行权,等待指定时间后自己醒来。不会释放所持有对象的锁。
- interrupt:1、打断处于等待状态的线程(对于阻塞状态的线程不起作用,如因synchronized方法或代码块等而阻塞的线程):在wait或sleep的线程、在等待线程结束的线程(join的调用者)可以被中断(叫打断更贴切,fuck翻译),如果被中断,会抛出InterruptedException;2、对非等待状态的线程调用interrupt不会抛异常,需要手动检测线程状态并做相应处理。
两个示例:
1 //打断处于等待状态的线程 2 public class InterruputSleepThread { 3 public static void main(String[] args) throws InterruptedException { 4 Thread t1 = new Thread() { 5 @Override 6 public void run() { 7 //while在try中,通过异常中断就可以退出run循环 8 try { 9 while (true) { 10 //当前线程处于阻塞状态,异常必须捕捉处理,无法往外抛出 11 TimeUnit.SECONDS.sleep(2); 12 } 13 } catch (InterruptedException e) { 14 System.out.println("Interruted When Sleep"); 15 boolean interrupt = this.isInterrupted(); 16 //中断状态被复位 17 System.out.println("interrupt:"+interrupt); 18 } 19 } 20 }; 21 t1.start(); 22 TimeUnit.SECONDS.sleep(2); 23 //中断处于阻塞状态的线程 24 t1.interrupt(); 25 26 /** 27 * 输出结果: 28 Interruted When Sleep 29 interrupt:false 30 */ 31 } 32 } 33 34 35 36 37 38 //对处于运行状态的线程标记中断 39 public class InterruputThread { 40 public static void main(String[] args) throws InterruptedException { 41 Thread t1=new Thread(){ 42 @Override 43 public void run(){ 44 while(true){ 45 //判断当前线程是否被中断 46 if (this.isInterrupted()){ 47 System.out.println("线程中断"); 48 break; 49 } 50 } 51 52 System.out.println("已跳出循环,线程中断!"); 53 } 54 }; 55 t1.start(); 56 TimeUnit.SECONDS.sleep(2); 57 t1.interrupt(); 58 59 /** 60 * 输出结果: 61 线程中断 62 已跳出循环,线程中断! 63 */ 64 } 65 } 66 67 68 //阻塞状态的线程打断无效 69 public class SynchronizedBlocked implements Runnable{ 70 71 public synchronized void f() { 72 System.out.println("Trying to call f()"); 73 while(true) // Never releases lock 74 Thread.yield(); 75 } 76 77 /** 78 * 在构造器中创建新线程并启动获取对象锁 79 */ 80 public SynchronizedBlocked() { 81 //该线程已持有当前实例锁 82 new Thread() { 83 public void run() { 84 f(); // Lock acquired by this thread 85 } 86 }.start(); 87 } 88 public void run() { 89 //中断判断 90 while (true) { 91 if (Thread.interrupted()) { 92 System.out.println("中断线程!!"); 93 break; 94 } else { 95 f(); 96 } 97 } 98 } 99 100 101 public static void main(String[] args) throws InterruptedException { 102 SynchronizedBlocked sync = new SynchronizedBlocked(); 103 Thread t = new Thread(sync); 104 //启动后调用f()方法,无法获取当前实例锁处于等待状态 105 t.start(); 106 TimeUnit.SECONDS.sleep(1); 107 //中断线程,无法生效 108 t.interrupt(); 109 } 110 }
- yield:Thread的静态方法,此方法只是使当前线程重新回到可执行状态,不会阻塞线程,因此执行yield()的线程有可能在进入到可执行状态后马上又被执行。实际上,当某个线程调用了yield方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的处于就绪状态的线程才会获得执行的机会。
- suspend:已过时,弃用
- resume:已过时,弃用
- stop:已过时,弃用
wait、notify、notifyAll三者的异同:(可参阅 彻底理解wait、notify、notifyAll)假设有对象A,线程T
- 是Object类的实例方法,且是native方法
- T调用A的这些方法时必须先拥有A的对象锁,即只能在同步方法或同步块中调用这些方法。否则会抛出IllegalMonitorStateException(是RuntimeException故无需try catch)
- 调用wait方法并不是立马释放锁,要等同步方法或同步块执行完;同理,调用notify、notifyAll并不会有线程立马获取到对象锁,需要等调用者所在同步方法或同步块执行完。
- 可以理解为每个对象有个wait set(等待池)和monitor set(锁池)用来存放线程:wait set中线程等待收到通知信号(notify、notifyAll)、不会竞争获取A的对象锁,而monitor set中的线程则竞争对象锁;T调用A的wait方法则T进入wait set、T调用notify或notifyAll则wait set中的一个线程(随机选)或所有线程进入monitor set竞争对象锁。(从这可看出,第一个获得对象锁的wait线程执行完后,若没有继续调用该对象的notify或notifyAll,且monitor set中没有线程,则其他wait线程仍一直等待,即便对该对象锁已经空闲)
wait和notify配合执行原理示例: