wait方法的并发编程作用是使当前执行代码的线程进行等待,它是彻制Object类的方法,该方法用来将当前线程置入等待队列中,底理并且在wait所在的解Jy机代码行处停止执行,直到接到通知或被中断为止。并发编程
该方法只能在同步方法或同步块中调用(即需要先获得对象的彻制监视器锁,一般来说在 synchronized 代码块或者synchronized修饰的底理方法中使用),否则抛出异常
IllegalMonitorStateException。解Jy机
在A线程中调用Lock对象的并发编程wait方法以后,会释放Lock对象的彻制监视器锁,同时将A线程放置于Lock对象的底理等待队列,A线程进入WAITING状态(Thread状态查看系列一)。解Jy机
notify/notifyAll方法notify/notifyAll方法的并发编程作用是唤醒执行对象的等待列表中的一个/所有线程,将其唤醒继续工作。彻制
同样的底理,notify/notifyAll方法也只能在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的云服务器提供商监视器锁。
在B线程中调用Lock对象的notify/notifyAll方法以后,Lock对象等待队列中的A线程从WAITING状态进入BLOCKED状态,而不是直接进入RUNNABLE状态。只有等B线程释放了Lock对象的监视器锁,并且A线程拿到以后,才进入到RUNNABLE状态。所以在编程中,尽量在使用了notify/notifyAll() 后立即释放对象的监视器锁,以便让其他线程获得锁进入RUNNABLE状态。
其他注意事项wait、notify/notifyAll 方法是Object的本地final方法,无法被重写。
notify 和wait 的顺序不能错,如果A线程先执行notify方法,B线程再执行wait方法,那么B线程是无法被唤醒的。
实例详解
public class WaitThread extends Thread { private Object lock; public WaitThread(Object lock) { this.lock = lock; } @Override public void run() { synchronized (lock) { System.out.println("WaitThread开始执行run方法"); try { System.out.println("WaitThread开始执行wait方法"); lock.wait(); System.out.println("WaitThread被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class NotifyThread extends Thread { private Object lock; public NotifyThread(Object lock) { this.lock = lock; } @Override public void run() { System.out.println("NotifyThread开始执行run方法"); try { System.out.println("NotifyThread睡眠2秒"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock) { System.out.println("NotifyThread开始执行notify方法"); lock.notify(); try { System.out.println("NotifyThread执行notify方法后继续睡眠2秒"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("NotifyThread睡眠2秒结束,并释放对象监视器锁"); } } } public class Main { public static void main(String[] args) { Object lock = new Object(); // 创建2个线程 WaitThread waitThread = new WaitThread(lock); NotifyThread notifyThread = new NotifyThread(lock); // 启动他们 waitThread.start(); notifyThread.start(); } } NotifyThread开始执行run方法 WaitThread开始执行run方法 WaitThread开始执行wait方法 NotifyThread睡眠2秒 NotifyThread开始执行notify方法 NotifyThread执行notify方法后继续睡眠2秒 NotifyThread睡眠2秒结束,并释放对象监视器锁 WaitThread被唤醒WaitThread在拿到lock的监视器锁以后调用wait方法。
NotifyThread在启动以后先睡眠2秒,保证了notify方法在wait方法后面。云服务器
NotifyThread执行notify方法后继续睡眠2秒,这个时候NotifyThread并没有释放Lock的监视器锁,因此WaitThread处于BLOCKED状态并没有被真正被唤醒。
NotifyThread睡眠2秒结束,并释放对象监视器锁,这个时候NotifyThread取到Lock的监视器锁并进入到RUNNABLE状态。
如果A线程获得了多个对象的监视器锁,然后调用其中1个对象的wait方法,是释放所有对象的锁还是只释放调用的那个对象的锁呢?
我们一起通过一个示例来进行一下测试。
public class WaitThread extends Thread { private Object lock; private Object other; public WaitThread(Object lock, Object other) { this.lock = lock; this.other = other; } @Override public void run() { synchronized (lock) { synchronized (other) { System.out.println("WaitThread开始执行run方法"); try { System.out.println("WaitThread开始执行wait方法"); lock.wait(); System.out.println("WaitThread被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public class NotifyThread extends Thread { private Object lock; private Object other; public NotifyThread(Object lock, Object other) { this.lock = lock; this.other = other; } @Override public void run() { System.out.println("NotifyThread开始执行run方法"); try { System.out.println("NotifyThread睡眠2秒"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock) { System.out.println("NotifyThread获得lock锁"); synchronized (other) { System.out.println("NotifyThread获得other锁"); System.out.println("NotifyThread开始执行notify方法"); lock.notify(); try { System.out.println("NotifyThread执行notify方法后继续睡眠2秒"); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("NotifyThread睡眠2秒结束,并释放对象监视器锁"); } } } } public class Main { public static void main(String[] args) { Object lock = new Object(); Object other = new Object(); // 创建2个线程 WaitThread waitThread = new WaitThread(lock, other); NotifyThread notifyThread = new NotifyThread(lock, other); // 启动他们 waitThread.start(); notifyThread.start(); } } WaitThread开始执行run方法 WaitThread开始执行wait方法 NotifyThread开始执行run方法 NotifyThread睡眠2秒 NotifyThread获得lock锁WaitThread线程拿到lock和other的对象锁以后,执行了lock的wait方法。NotifyThread线程在睡眠2秒后,仅拿到了lock锁,说明wait方法只释放了被执行对应的锁,这样就造成了死锁。
因此如果使用wait和notify机制时,一定要确认Wait线程和Notify线程获取对象锁的情况,尽量避免在获取多个对象锁的情况下使用,源码下载防止造成死锁问题。