这是对
的再次升级分析。
之前虽然可以进行线程间的唤醒,但是,都是一个线程借助一个锁,唤醒另外一个线程。
但是,我们想多个线程借助一个模块,唤醒多个对应线程。
设计一个用来解耦的中间类,不仅能够解耦「结果等待者」和「结果生产者」,同时还能够同时支持多个任务的管理。
考虑以下的场景
- 有一个相亲晚会,相亲晚会的一个节目是,女生收到男生的情书
- 每个女生只能收到一个情书
- 男生只能有一个心仪的女生,并且写情书,为了公平,男生选择好一个女生后,其他男生就不能选择该女生了
- 在规定时间内,男生把情书写好,放到信箱中,等待心仪女生获取
上面只是一个小场景,不必特别在意「其实是为了代码逻辑,硬想出的场景」,但是,有一个限制点需要特别提示一下
代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.Hashtable; import java.util.Map; import java.util.Set;
@Slf4j(topic = "c.Run") public class Run {
public static void main(String[] args) throws Exception { for (int i = 0; i < 3; i++) { new girl().start(); } Thread.sleep(1000);
for (Integer id : Mailboxes.getIds()) { new boy(id, "情书内容:做我女朋友吧" + id).start(); } }
}
@Slf4j(topic = "c.Girl") class girl extends Thread { @Override public void run() { GuardedObject guardedObject = Mailboxes.createGuardedObject(); log.debug("等待情书 女生 id {}", guardedObject.getId()); Object mail = guardedObject.get(5000); log.debug("收到情书 女生 id:{} 内容:{}", guardedObject.getId(), mail); } }
@Slf4j(topic = "c.Boy") class boy extends Thread { private int id; private String mail;
public boy(int id, String mail) { this.id = id; this.mail = mail; }
@Override public void run() { GuardedObject guardedObject = Mailboxes.getGuardedObject(id); log.debug("写情书并发送 id:{} 内容 {}", id, mail); guardedObject.complete(mail); } }
class Mailboxes { private static Map<Integer, GuardedObject> boxes = new Hashtable<>();
private static int id = 1;
private static synchronized int generateId() { return id++; }
public static GuardedObject getGuardedObject(int id) { return boxes.remove(id); }
public static GuardedObject createGuardedObject() { GuardedObject go = new GuardedObject(generateId()); boxes.put(go.getId(), go); return go; }
public static Set<Integer> getIds() { return boxes.keySet(); } }
class GuardedObject { private int id; private Object response;
public GuardedObject(int id) { this.id = id; }
public int getId() { return id; }
public Object get(long timeout) { long begin = System.currentTimeMillis(); synchronized (this) { long passedTime = 0; while (response == null) { if (passedTime >= timeout) { break; } try { this.wait(timeout - passedTime); } catch (InterruptedException e) { e.printStackTrace(); } passedTime = System.currentTimeMillis() - begin; } return response; } }
public void complete(Object response) { synchronized (this) { this.response = response; this.notifyAll(); } } }
|
输出
1 2 3 4 5 6 7 8 9
| 14:30:16.034 [Thread-2] DEBUG c.Girl - 等待情书 女生 id 2 14:30:16.035 [Thread-1] DEBUG c.Girl - 等待情书 女生 id 3 14:30:16.034 [Thread-0] DEBUG c.Girl - 等待情书 女生 id 1 14:30:17.048 [Thread-4] DEBUG c.Boy - 写情书并发送 id:2 内容 情书内容:做我女朋友吧2 14:30:17.048 [Thread-3] DEBUG c.Boy - 写情书并发送 id:3 内容 情书内容:做我女朋友吧3 14:30:17.049 [Thread-5] DEBUG c.Boy - 写情书并发送 id:1 内容 情书内容:做我女朋友吧1 14:30:17.049 [Thread-1] DEBUG c.Girl - 收到情书 女生 id:3 内容:情书内容:做我女朋友吧3 14:30:17.049 [Thread-2] DEBUG c.Girl - 收到情书 女生 id:2 内容:情书内容:做我女朋友吧2 14:30:17.049 [Thread-0] DEBUG c.Girl - 收到情书 女生 id:1 内容:情书内容:做我女朋友吧1
|
根据
Mailboxes
是图中的 Futures
MailBoxes
暂时存放中间信息传递
这时候有的人就问,必须要 MailBoxes 吗?答案是当然可以,看下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
@Slf4j(topic = "c.Run") public class Run {
public static void main(String[] args) throws Exception { List<girl> girls = new ArrayList<>(); for (int i = 0; i < 3; i++) { girl girl_ = new girl(); girls.add(girl_); girl_.start(); } Thread.sleep(1000);
girls.forEach( (g) -> { new boy(g, "做我女朋友").start(); } ); }
}
@Slf4j(topic = "c.Girl") class girl extends Thread {
private GuardedObject guardedObject = new GuardedObject();
public GuardedObject getGuardedObject() { return guardedObject; }
@Override public void run() { log.debug("等待情书 女生"); Object mail = guardedObject.get(5000); log.debug("收到情书 女生 内容:{}", mail); } }
@Slf4j(topic = "c.Boy") class boy extends Thread { private String mail; private girl girl_;
public boy(girl girl_, String mail) { this.girl_ = girl_; this.mail = mail; }
@Override public void run() { log.debug("写情书并发送 内容 {}", mail); this.girl_.getGuardedObject().complete(mail); } }
class GuardedObject { private Object response;
public Object get(long timeout) { long begin = System.currentTimeMillis(); synchronized (this) { long passedTime = 0; while (response == null) { if (passedTime >= timeout) { break; } try { this.wait(timeout - passedTime); } catch (InterruptedException e) { e.printStackTrace(); } passedTime = System.currentTimeMillis() - begin; } return response; } }
public void complete(Object response) { synchronized (this) { this.response = response; this.notifyAll(); } } }
|
功能当然可以实现,但是,耦合性非常高,不利于以后的功能替换。