定义
同步模式之保护性暂停即
Guarded Suspension,用在一个线程等待另一个线程的执行结果。有一个结果需要从一个线程传递到另一个线程,让他们关联同一个
GuardedObject如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)JDK中,join的实现、Future的实现,采用的就是此模式。
实现
@Slf4j
public class Test {
public static void main(String[] args) {
GuardedObject guardedObject = new GuardedObject();
//线程一等待线程二的任务
new Thread(() -> {
//等待结果
String result = (String) guardedObject.getResponse();
log.info("t1领取到的奖状是{}", result);
}, "t1线程").start();
//线程二的任务
new Thread(() -> {
log.info("t2线程去帮t1线程领奖");
guardedObject.setResponse(new String("三好学生奖状"));
}, "t2线程").start();
}
}
class GuardedObject {
//结果
private Object response;
//获取结果的方法
public Object getResponse() {
synchronized (this) {
while (response == null) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
return response;
}
//生产结果的方法
public void setResponse(Object response) {
synchronized (this) {
this.response = response;
this.notify();
}
}
}
Park Unpark
该方法存在于
LockSupport类当中,park()和unpark()的作用分别是阻塞线程和解除阻塞线程。三种让线程等待和唤醒的方法:
- 使用
Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程。- 使用
JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程。LockSupport类的park()和unpark()可以阻塞当前线程以及唤醒指定被阻塞的线程。
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("start...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.debug("执行阻塞park方法...");
LockSupport.park();
log.debug("线程一执行park方法完毕...");
});
t1.start();
Thread.sleep(1000);
log.debug("解除阻塞t1线程的unpark方法");
LockSupport.unpark(t1);
}
}
//执行结果
18:31:04.489 [Thread-0] DEBUG com.wuke.test.Test - start...
18:31:05.488 [main] DEBUG com.wuke.test.Test - 解除阻塞t1线程的unpark方法
18:31:06.500 [Thread-0] DEBUG com.wuke.test.Test - 执行阻塞park方法...
18:31:06.500 [Thread-0] DEBUG com.wuke.test.Test - 线程一执行park方法完毕...
与Object的wait和notify相比
wait¬ify¬ifyAll必须配合synchronized一起使用,而unpark则不需要。park&unpark是以线程为单位来进行阻塞与唤醒线程,而notify只能随机唤醒一个线程,notifyAll则是唤醒所有的线程,相比没那么精确。park&unpark可以先unpark,而wait¬ify不能先notify。
原理
LockSupport类使用了一种名为Permit(许可证)的概念来做到阻塞和唤醒线程的功能。
- 当调用
unpark()方法的时候会赋给当前线程一个Permit(许可证)。 - 当调用
park()方法的时候会判断当前线程有没有Permit(许可证),如果没有则进行阻塞。直到其拥有一个Permit(许可证)才执行,执行后将当前线程拥有的Permit许可证置空。
每个线程只有一个permit(许可证),这意味着即使我们多次执行unpark()方法,在执行一次park()方法后,该对象拥有的许可证为空。
但当多次执行park()方法,在执行一次unpark()方法后,则会阻塞。因为多次调用park()方法则需要多个许可证,而线程只有一个permit(许可证),permit(许可证)不足所以阻塞。