0%

java | 设计模式 同步模式-保护性暂停 优化 超时

这是对 java | 设计模式 同步模式-保护性暂停 的优化,增加了超时。

之前的代码,如果,返回迟迟不给,那么,就会一直等待。

代码如下

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
package com.redisc;

import lombok.extern.slf4j.Slf4j;

class Downloader {
public static String download() throws InterruptedException {
Thread.sleep(4000);
return "success";
}
};

@Slf4j(topic = "c.Run")
public class Run {

public static void main(String[] args) throws Exception {
// 线程1 等待 线程2 的下载结果
GuardedObject guardedObject = new GuardedObject();
new Thread(() -> {
log.debug("等待结果");
Object state = (String) guardedObject.get(2000);
log.debug("下载结果[{}]", state);
}, "t1").start();

new Thread(() -> {
log.debug("开始下载");
try {
String state = Downloader.download();
guardedObject.complete(state);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2").start();
}

}

class GuardedObject {
private Object response;

// 获取结果
public Object get(long timeout) {
// 开始时间
long begin = System.currentTimeMillis();
synchronized (this) {
// 经历时间
long passedTime = 0;
// 解决虚假唤醒
while (response == null) {
System.out.println(1);
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
12:25:32.171 [t2] DEBUG c.Run - 开始下载
12:25:32.171 [t1] DEBUG c.Run - 等待结果
1
1
12:25:34.179 [t1] DEBUG c.Run - 下载结果[null]

关于这个代码需要解释 2 个地方。

为什么输出两个 1

第一个 1 是因为,刚进入 while (response == null) 循环,输出 1

第二个 1 是因为,this.wait(timeout - passedTime); 倒计时事件结束,再次进入到 get 方法,输出第二个 1

为什么 this.wait(timeout - passedTime);

有的人想写成 this.wait(timeout);

考虑虚假唤醒问题,如果,有一个线程虚假唤醒了方法,那个进入到方法体内后,又要睡眠 timeout,如果极端情况,多次虚假唤醒,每次都要重新睡眠 timeout

请我喝杯咖啡吧~