定义:
即Guarded Suspension、用在一个线程等待另一个线程的执行结果
- 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个GuardedObject
- 如果有结果不断从一个线程到另一个线程可以使用消息队列(生产者/消费者)
- JDK中,join的实现、Future的实现,采用就是此模式
- 因为要等待另一方的结果,因此归类到同步模式
实现:
public class GuardedObject {
//结果
private Object response;
//获取结果
public Object get() throws InterruptedException {
synchronized (this){
//还没有结果
while(response==null){
this.wait();
}
}
return response;
}
public void complete(Object response){
synchronized (this){
//给结果成员变量赋值
this.response=response;
this.notifyAll();
}
}
}
GuardedObject类实现细节如上,一个成员变量用来存放存储结果,再自定义两个方法更改变量值。
具体实例:线程1等待线程2下载完毕内容,注意object相当于共享变量,所以类中方法要加入synchronized代码块。
import javax.imageio.IIOException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class GuardedObject {
//结果
private Object response;
//获取结果
public Object get() throws InterruptedException {
synchronized (this){
//还没有结果
while(response==null){
this.wait();
}
}
return response;
}
public void complete(Object response){
synchronized (this){
//给结果成员变量赋值
this.response=response;
this.notifyAll();
}
}
}
class Test20{
public static void main(String[] args) {
GuardedObject object = new GuardedObject();
new Thread(()->{
//等待结果
System.out.println("等待结果");
List<String> list = null;
try {
list = (List<String>) object.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
},"t1").start();
new Thread(()->{
System.out.println("执行下载");
List<String> list = null;
try {
list = Downloader.download();
} catch (IOException e) {
e.printStackTrace();
}
object.complete(list);
},"t2").start();
}
}
class Downloader{
public static List<String> download() throws IOException{
HttpURLConnection conn = (HttpURLConnection) new URL("www.baidu.com").openConnection();
List<String> lines =new ArrayList<>();
try(BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(),StandardCharsets.UTF_8))){
String line;
while ((line=reader.readLine())!=null){
lines.add(line);
}
}
return lines;
}
}
此种写法的好处,以往的join()方法,线程1需要用等待线程2完全执行结束才能拿到结果,但这是没必要的,完全可以在线程2生成结果后使得线程2继续执行。
所以这种带wait-notify的形式称为“保护性暂停”
扩展-增加超时:
当线程wait时候条件不得到满足,按照while-wait(空参)的写法,就会陷入空等,只有条件满足时线程才可以继续执行下去。所以要使用带参数的wait方法,控制等待的时间,如果时间满足则不再等待。
代码如下:
//获取结果
public Object get(int timeout) throws InterruptedException {
synchronized (this){
long begin = System.currentTimeMillis();
//定义经历的时间
long passedTime = 0;
while(response==null){
long waitTime = timeout-passedTime;
//经历的时间超过了最大等待时间,退出循环
if(waitTime<=0){
break;//跳出循环,不再等待
}
this.wait(waitTime);
passedTime = System.currentTimeMillis()-begin;//获得经历时间
}
}
return response;
}
设置一个passedTime,记录循环中等待的时间,一旦超时则通过break跳出循环,线程执行。但是一定要注意虚假唤醒的情况,如果wait位置被notifyAll虚假唤醒,一定要将之前等待的时间扣除,是根据waitTime进一步判断,而不是根据timeout值判断,这里一定要注意。