Java之Object对象中的wait()和notifyAll()用法

Posted jun1019

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java之Object对象中的wait()和notifyAll()用法相关的知识,希望对你有一定的参考价值。

用一个例子来说明Object对象中的wait方法和notifyAll方法的使用。

 

首先定义一个消息类,用于封装数据,以供读写线程进行操作:

 1 /**
 2  * 消息
 3  *
 4  * @author syj
 5  */
 6 public class Message 
 7 
 8     private String msg;
 9 
10     public String getMsg() 
11         return msg;
12     
13 
14     public void setMsg(String msg) 
15         this.msg = msg;
16     
17 

 

读线程,从Message对象中读取数据,如果没有数据,就一直阻塞等待:

 1 /**
 2  * 读线程
 3  *
 4  * @author syj
 5  */
 6 public class Reader implements Runnable 
 7 
 8     private Message message;
 9 
10     public Reader(Message message) 
11         this.message = message;
12     
13 
14     @Override
15     public void run() 
16         synchronized (message) 
17             try 
18                 // 务必加上该判断,否则可能会因某个读线程在写线程的 notifyAll() 之后执行,
19                 // 这将导致该读线程永远无法被唤醒,程序会一直被阻塞
20                 if (message.getMsg() == null) 
21                     message.wait();// 等待被 message.notify() 或 message.notifyAll() 唤醒
22                 
23              catch (InterruptedException e) 
24                 e.printStackTrace();
25             
26             // 读取 message 对象中的数据
27             System.out.println(Thread.currentThread().getName() + " - " + message.getMsg());
28         
29     
30 

 

写线程,往Message对象中写数据,写入成功就调用 message.notifyAll() 方法来唤醒在 message.wait() 上阻塞的线程(上面的读线程将被唤醒,读线程解除阻塞继续执行):

 1 import java.util.UUID;
 2 
 3 /**
 4  * 写线程
 5  *
 6  * @author syj
 7  */
 8 public class Writer implements Runnable 
 9 
10     private Message message;
11 
12     public Writer(Message message) 
13         this.message = message;
14     
15 
16     @Override
17     public void run() 
18         synchronized (message) 
19             try 
20                 Thread.sleep(1000L);// 模拟业务耗时
21              catch (InterruptedException e) 
22                 e.printStackTrace();
23             
24             // 向 message 对象中写数据
25             message.setMsg(Thread.currentThread().getName() + ":" + UUID.randomUUID().toString().replace("-", ""));
26             message.notifyAll();// 唤醒所有 message.wait()
27         
28     
29 

 

测试类,启动两个读线程,从Message对象中读取数据,启动一个写线程,往Message对象中写数据:

 1 /**
 2  * 测试 Object 对象中的 wait()/notifyAll() 用法
 3  *
 4  * @author syj
 5  */
 6 public class LockApp 
 7     public static void main(String[] args) 
 8         Message message = new Message();
 9         new Thread(new Reader(message), "R1").start();// 读线程 名称 R1
10         new Thread(new Reader(message), "R2").start();// 读线程 名称 R2
11         new Thread(new Writer(message), "W").start();// 写线程 名称 W
12     
13 

 

测试结果:

R2 - W:4840dbd6b312489a9734414dd99a4bcb
R1 - W:4840dbd6b312489a9734414dd99a4bcb

 

这个特性有什么用?

显然,在同步等待异步处理结果的场景中,这个特性就很有用处了。比如,在RPC框架中,Netty服务器返回结果是异步的,Netty客户端如何拿到这个异步结果呢?

 

 1 import java.util.UUID;
 2 import java.util.concurrent.ConcurrentHashMap;
 3 
 4 /**
 5  * 使用 Object对象的 wait() 和 notifyAll() 实现同步等待异步结果
 6  *
 7  * @author syj
 8  */
 9 public class App 
10 
11     public static void main(String[] args) 
12         // 请求唯一标识
13         String requestId = UUID.randomUUID().toString();
14         App app = new App();
15 
16         new Thread(new Runnable() 
17             @Override
18             public void run() 
19                 try 
20                     Thread.sleep(2000L);// 模拟业务耗时
21                  catch (InterruptedException e) 
22                     e.printStackTrace();
23                 
24                 // 写入数据
25                 app.set(requestId, UUID.randomUUID().toString().replace("-", ""));
26             
27         ).start();
28 
29         String message = app.get(requestId);
30         System.out.println(message);
31     
32 
33 
34     // ------------------------ 异步结果 --------------------------
35 
36     // 用于存放异步结果, key是请求ID, value是异步结果
37     private static ConcurrentHashMap<String, String> resultMap = new ConcurrentHashMap<>();
38     private Object lock = new Object();
39 
40     /**
41      * 写数据到 resultMap,写入成功唤醒所有在 lock 对象上等待的线程
42      *
43      * @param requestId
44      * @param message
45      */
46     public void set(String requestId, String message) 
47         resultMap.put(requestId, message);
48         synchronized (lock) 
49             lock.notifyAll();
50         
51     
52 
53     /**
54      * 从 resultMap 中读数据,如果没有数据则等待
55      *
56      * @param requestId
57      * @return
58      */
59     public String get(String requestId) 
60         synchronized (lock) 
61             try 
62                 if (resultMap.get(requestId) == null) 
63                     lock.wait();
64                 
65              catch (InterruptedException e) 
66                 e.printStackTrace();
67             
68         
69         return resultMap.get(requestId);
70     
71 

 

参考文章:https://cloud.tencent.com/developer/article/1155102

 

以上是关于Java之Object对象中的wait()和notifyAll()用法的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程基础之wait,notify

Java 多线程编程之:notify 和 wait 用法

Java之sleep和wait

Java之sleep和wait

Java Object对象中的wait,notify,notifyAll的理解

Java线程间通信之wait/notify