什么是保护性暂停模式
保护性暂停模式就是提供了一种线程间通信能力的模式。
如果有一个线程的执行结果需要传递给另一个线程,就需要使用保护性暂停模式将两条线程关联起来。
JDK中join方法和Future就是使用了此模式实现的。
保护性暂停模式的实现
package com.leolee.multithreadProgramming.concurrent.guarded;
import lombok.extern.slf4j.Slf4j;
/**
* @ClassName Test
* @Description: 测试Gurarded
* Gurarded的作用:
* 如果有一个线程的结果需要传递到另一个线程,让他们用Gurarded做关联
* JDK中join和future就是使用此模式实现的
* @Author LeoLee
* @Date 2020/12/5
* @Version V1.0
**/
@Slf4j
public class Test {
public static void main(String[] args) {
GuardedObject guardedObject = new GuardedObject();
new Thread(() -> {
log.info("{} is waiting for response", Thread.currentThread().getName());
log.info("response:{}", Boolean.valueOf(String.valueOf(guardedObject.getResponse())));
}, "t1").start();
new Thread(() -> {
log.info("{} executing something for response", Thread.currentThread().getName());
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
guardedObject.complete(true);
}, "t2").start();
}
}
class GuardedObject {
//之后传递的结果
private Object response;
//获取结果的方法
public Object getResponse() {
synchronized (this) {
//response为空则进入等待,当有其他线程已经产生了结果并对response赋值之后,则唤醒该线程
while (response == null) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
public Object getResponse(long timeout) {
synchronized (this) {
//开始等待的时间
long beginTime = System.currentTimeMillis();
//已经等待的时间
long processTime = 0;
//response为空则进入等待,当有其他线程已经产生了结果并对response赋值之后,则唤醒该线程
while (response == null) {
//防止虚假唤醒(就是防止其他线程唤起该线程的时候,response还没有值)
//所以这里是等待timeout - processTime,即等待剩余没有等待的时间
long waitTIme = timeout - processTime;
if (waitTIme <= 0) {
break;
}
try {
this.wait(waitTIme);
} catch (InterruptedException e) {
e.printStackTrace();
}
processTime = System.currentTimeMillis() - beginTime;
}
return response;
}
}
//产生结果
public void complete(Object response) {
synchronized (this) {
this.response = response;
this.notifyAll();
}
}
}
join的源码是典型的保护性暂停模式:
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}