Java并发编程之二彻底理解Java的wait和Notify机制

开发 后端
wait方法的作用是使当前执行代码的线程进行等待,它是Object类的方法,该方法用来将当前线程置入等待队列中,并且在wait所在的代码行处停止执行,直到接到通知或被中断为止。

 [[410508]]

简单介绍

  • wait方法

wait方法的作用是使当前执行代码的线程进行等待,它是Object类的方法,该方法用来将当前线程置入等待队列中,并且在wait所在的代码行处停止执行,直到接到通知或被中断为止。

该方法只能在同步方法或同步块中调用(即需要先获得对象的监视器锁,一般来说在 synchronized 代码块或者synchronized修饰的方法中使用),否则抛出异常

IllegalMonitorStateException。

在A线程中调用Lock对象的wait方法以后,会释放Lock对象的监视器锁,同时将A线程放置于Lock对象的等待队列,A线程进入WAITING状态(Thread状态查看系列一)。

  • 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线程是无法被唤醒的。

实例详解

  1. public class WaitThread extends Thread { 
  2.  
  3.     private Object lock; 
  4.  
  5.     public WaitThread(Object lock) { 
  6.         this.lock = lock; 
  7.     } 
  8.  
  9.     @Override 
  10.     public void run() { 
  11.         synchronized (lock) { 
  12.             System.out.println("WaitThread开始执行run方法"); 
  13.             try { 
  14.                 System.out.println("WaitThread开始执行wait方法"); 
  15.                 lock.wait(); 
  16.                 System.out.println("WaitThread被唤醒"); 
  17.             } catch (InterruptedException e) { 
  18.                 e.printStackTrace(); 
  19.             } 
  20.         } 
  21.     } 
  1. public class NotifyThread extends Thread { 
  2.  
  3.     private Object lock; 
  4.  
  5.     public NotifyThread(Object lock) { 
  6.         this.lock = lock; 
  7.     } 
  8.  
  9.     @Override 
  10.     public void run() { 
  11.         System.out.println("NotifyThread开始执行run方法"); 
  12.         try { 
  13.             System.out.println("NotifyThread睡眠2秒"); 
  14.             Thread.sleep(2000); 
  15.         } catch (InterruptedException e) { 
  16.             e.printStackTrace(); 
  17.         } 
  18.         synchronized (lock) { 
  19.             System.out.println("NotifyThread开始执行notify方法"); 
  20.             lock.notify(); 
  21.             try { 
  22.                 System.out.println("NotifyThread执行notify方法后继续睡眠2秒"); 
  23.                 Thread.sleep(2000); 
  24.             } catch (InterruptedException e) { 
  25.                 e.printStackTrace(); 
  26.             } 
  27.             System.out.println("NotifyThread睡眠2秒结束,并释放对象监视器锁"); 
  28.         } 
  29.     } 
  1. public class Main { 
  2.     public static void main(String[] args) { 
  3.  
  4.         Object lock = new Object(); 
  5.  
  6.         // 创建2个线程 
  7.         WaitThread waitThread = new WaitThread(lock); 
  8.         NotifyThread notifyThread = new NotifyThread(lock); 
  9.         // 启动他们 
  10.         waitThread.start(); 
  11.         notifyThread.start(); 
  12.     } 
  1. NotifyThread开始执行run方法 
  2. WaitThread开始执行run方法 
  3. WaitThread开始执行wait方法 
  4. NotifyThread睡眠2秒 
  5. NotifyThread开始执行notify方法 
  6. NotifyThread执行notify方法后继续睡眠2秒 
  7. NotifyThread睡眠2秒结束,并释放对象监视器锁 
  8. WaitThread被唤醒 

WaitThread在拿到lock的监视器锁以后调用wait方法。

NotifyThread在启动以后先睡眠2秒,保证了notify方法在wait方法后面。

NotifyThread执行notify方法后继续睡眠2秒,这个时候NotifyThread并没有释放Lock的监视器锁,因此WaitThread处于BLOCKED状态并没有被真正被唤醒。

NotifyThread睡眠2秒结束,并释放对象监视器锁,这个时候NotifyThread取到Lock的监视器锁并进入到RUNNABLE状态。

进阶思考

如果A线程获得了多个对象的监视器锁,然后调用其中1个对象的wait方法,是释放所有对象的锁还是只释放调用的那个对象的锁呢?

我们一起通过一个示例来进行一下测试。

  1. public class WaitThread extends Thread { 
  2.  
  3.     private Object lock; 
  4.     private Object other; 
  5.  
  6.     public WaitThread(Object lock, Object other) { 
  7.         this.lock = lock; 
  8.         this.other = other; 
  9.     } 
  10.  
  11.     @Override 
  12.     public void run() { 
  13.         synchronized (lock) { 
  14.             synchronized (other) { 
  15.                 System.out.println("WaitThread开始执行run方法"); 
  16.                 try { 
  17.                     System.out.println("WaitThread开始执行wait方法"); 
  18.                     lock.wait(); 
  19.                     System.out.println("WaitThread被唤醒"); 
  20.                 } catch (InterruptedException e) { 
  21.                     e.printStackTrace(); 
  22.                 } 
  23.             } 
  24.         } 
  25.     } 
  1. public class NotifyThread extends Thread { 
  2.  
  3.     private Object lock; 
  4.     private Object other; 
  5.  
  6.     public NotifyThread(Object lock, Object other) { 
  7.         this.lock = lock; 
  8.         this.other = other; 
  9.     } 
  10.  
  11.     @Override 
  12.     public void run() { 
  13.         System.out.println("NotifyThread开始执行run方法"); 
  14.         try { 
  15.             System.out.println("NotifyThread睡眠2秒"); 
  16.             Thread.sleep(2000); 
  17.         } catch (InterruptedException e) { 
  18.             e.printStackTrace(); 
  19.         } 
  20.         synchronized (lock) { 
  21.             System.out.println("NotifyThread获得lock锁"); 
  22.  
  23.             synchronized (other) { 
  24.                 System.out.println("NotifyThread获得other锁"); 
  25.                 System.out.println("NotifyThread开始执行notify方法"); 
  26.                 lock.notify(); 
  27.                 try { 
  28.                     System.out.println("NotifyThread执行notify方法后继续睡眠2秒"); 
  29.                     Thread.sleep(2000); 
  30.                 } catch (InterruptedException e) { 
  31.                     e.printStackTrace(); 
  32.                 } 
  33.                 System.out.println("NotifyThread睡眠2秒结束,并释放对象监视器锁"); 
  34.             } 
  35.         } 
  36.     } 
  1. public class Main { 
  2.     public static void main(String[] args) { 
  3.  
  4.         Object lock = new Object(); 
  5.         Object other = new Object(); 
  6.  
  7.         // 创建2个线程 
  8.         WaitThread waitThread = new WaitThread(lock, other); 
  9.         NotifyThread notifyThread = new NotifyThread(lock, other); 
  10.         // 启动他们 
  11.         waitThread.start(); 
  12.         notifyThread.start(); 
  13.     } 
  1. WaitThread开始执行run方法 
  2. WaitThread开始执行wait方法 
  3. NotifyThread开始执行run方法 
  4. NotifyThread睡眠2秒 
  5. NotifyThread获得lock锁 

WaitThread线程拿到lock和other的对象锁以后,执行了lock的wait方法。NotifyThread线程在睡眠2秒后,仅拿到了lock锁,说明wait方法只释放了被执行对应的锁,这样就造成了死锁。

因此如果使用wait和notify机制时,一定要确认Wait线程和Notify线程获取对象锁的情况,尽量避免在获取多个对象锁的情况下使用,防止造成死锁问题。

责任编辑:武晓燕 来源: 今日头条
相关推荐

2010-03-15 18:25:27

Java编程语言

2023-10-08 09:34:11

Java编程

2020-07-06 08:03:32

Java悲观锁乐观锁

2017-09-19 14:53:37

Java并发编程并发代码设计

2020-12-11 07:32:45

编程ThreadLocalJava

2020-11-13 08:42:24

Synchronize

2012-03-09 10:44:11

Java

2015-08-05 09:33:21

Javawaitnotify

2020-12-08 08:53:53

编程ThreadPoolE线程池

2021-03-10 15:59:39

JavaSynchronize并发编程

2019-11-07 09:20:29

Java线程操作系统

2024-06-21 08:32:24

2020-12-15 08:06:45

waitnotifyCondition

2024-05-17 10:05:06

Java机制应用

2016-09-26 17:09:28

Java并发编程内存模型

2021-10-11 11:58:41

Channel原理recvq

2022-02-21 08:20:24

Wait方法Notify

2020-12-10 07:00:38

编程线程池定时任务

2011-07-21 10:17:53

java

2024-03-15 08:23:26

异步编程函数
点赞
收藏

51CTO技术栈公众号