用一个例子来说明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
原文地址:https://www.cnblogs.com/jun1019/p/10965133.html
时间: 2024-11-07 23:11:55