定义
即 Guarded Suspension,用在一个线程等待另一个线程的执行结果
要点
- 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject
- 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)
- JDK 中,join 的实现、Future 的实现,采用的就是此模式
- 因为要等待另一方的结果,因此归类到同步模式
join示例:
public static void main(String[] args){
Thread t = new Thread(()->{
Thread.sleep(1000);
});
t.join();
System.out.println("1000");
}
Future示例:
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
System.out.println("hello");
return 100;
});
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
System.out.println("结果是:"+result);
}
join()的原理
// 无参数的join,调用join(0)
public final void join() throws InterruptedException {
join(0);
}
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");
}
// 无参的join方法会调用join(0),从而进入该分支
if (millis == 0) {
// 判断线程是否还存活,存活则调用wait等待
while (isAlive()) {
wait(0);
}
} else {
// 判断线程是否还存活
while (isAlive()) {
// 计算剩余时间
long delay = millis - now;
// <=0 表示join等待已经超时,退出等待
if (delay <= 0) {
break;
}
// 使用带有时间的wait方法等待
wait(delay);
// 计算已过去的时间
now = System.currentTimeMillis() - base;
}
}
}
// native方法,它会使线程进入等待,直到通过notify唤醒或者超时
public final native void wait(long timeout) throws InterruptedException;
public final native boolean isAlive();
通过阅读源码我们发现,如果我们调用无参数的join其实是调用了join(0),所以我们直接看有参数的join方法。
该方法分为两个分支,一个是millis=0,即无限时等待。另一个是millis>0,即限时等待。
/**
* TestGuardedObject
* <p>
* join原理
* </p>
* encoding:UTF-8
*
* @author lisonglin 23:36 2022/3/4
*/
public class TestGuardedObject {
public static void main(String[] args) {
GuardedObject v2 = new GuardedObject();
new Thread(() -> {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
v2.complete(null);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
v2.complete(Arrays.asList("a", "b", "c"));
}).start();
Object response = v2.get(2500);
if (response != null) {
System.out.println("get response: "+((List<String>) response).size()+"lines");
} else {
System.out.println("can't get response");
}
}
}
class GuardedObject {
private Object response;
private final Object lock = new Object();
public Object get(){
return get(0);
}
public Object get(long millis) {
synchronized (lock) {
long base = System.currentTimeMillis();
long now = 0;
if (millis == 0) {
while (response == null) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} else {
while (response == null) {
long delay = millis - now;
System.out.println("waitTime:" + delay);
if (delay <= 0) {
System.out.println("break...");
break;
}
try {
lock.wait(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
now = System.currentTimeMillis() - base;
System.out.println("timePassed:" + now + ", object is null");
}
}
return response;
}
}
public void complete(Object response) {
synchronized (lock) {
this.response = response;
System.out.println("notify...");
lock.notifyAll();
}
}
}
waitTime:2500 notify... timePassed:1, object is null waitTime:2499 notify... timePassed:3, object is null get response: 3lines