JUC--设计模式_保护性暂停

JUC–保护性暂停

  • JUC–保护性暂停

  • 博主以黑马JUC进行学习

定义

即Guarded Suspension,用一个线程等待另一个线程的执行结果

要点

  • 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个GuardedObject
  • 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)
  • JDK中,join的实现、Future的实现,采用的就是此模式
  • 因为要等待另一方的结果,因此归类到同步模式
  • 底层就是:wait()+ notify()

实现

GuardedObject

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
public class GuardedObject {
// 要传递的结果
private Object response;

// 获取结果:没有结果就等待
public Object get() {
synchronized (this) {
// 防止虚假唤醒,必须用 while
while (response == null) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}

// 产生结果:设置完成后唤醒等待线程
public void complete(Object response) {
synchronized (this) {
this.response = response;
this.notifyAll();
}
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test {
public static void main(String[] args) {
GuardedObject go = new GuardedObject();

// 线程1:等待结果
new Thread(() -> {
System.out.println("等待结果...");
Object res = go.get();
System.out.println("收到结果:" + res);
}).start();

// 线程2:生产结果
new Thread(() -> {
try {
Thread.sleep(1000); // 模拟干活
} catch (InterruptedException e) {}

go.complete("我是执行结果!");
System.out.println("已产生结果");
}).start();
}
}

扩展_增加超时

不让线程无限等,最多等一段时间,没结果就返回null

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
public class GuardedObject {
private Object response;

// 超时等待:maxWait 最大等待毫秒数
public Object get(long maxWait) {
long begin = System.currentTimeMillis();
long waitTime = 0; // 已经等待的时间

synchronized (this) {
while (response == null) {
long needWait = maxWait - waitTime;
if (needWait <= 0) {
break; // 超时,退出等待
}

try {
this.wait(needWait); // 只等待剩余时间
} catch (InterruptedException e) {}

waitTime = System.currentTimeMillis() - begin;
}
return response;
}
}

public void complete(Object response) {
synchronized (this) {
this.response = response;
this.notifyAll();
}
}
}

使用

1
Object res = go.get(2000); // 最多等2秒

扩展_解耦等待和生产

如果需要在多个类之间使用GuardedObject对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类,这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理

  • 一般为一对多

设计思路:

  • 问题:直接用 GuardedObject 必须传递同一个对象,类与类之间耦合严重。

  • 解耦:

    增加一个中间容器类(我们叫它 GuardedObjectBox / TaskManager):

    • 等待者:只需要任务 ID去等待结果
    • 生产者:只需要任务 ID去生产结果
    • 两者不直接接触、不互相传递对象、不互相依赖
  • 结构:

    • GuardedObject:基础等待 / 通知对象
    • TaskManager:中间解耦容器(核心)
    • 等待线程:从 TaskManager 获取并等待
    • 生产线程:从 TaskManager 获取并赋值