这是对 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 { 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
。