想象一个场景,你去蛋糕店买蛋糕,先下订单之后,店员给你一张提货单,叫你下午来取货,下午你来取蛋糕,如果此时蛋糕已经做好了,则拿走蛋糕,如果没有做好,则你还得再等等。相对应的程序场景,主线程要得到某些数据需要耗时操作,于是开了个子线程去生产数据,然后主线程去做别的事,等一段时间之后再去取回数据,实现了异步回调。
看代码~~
首选是Data接口,表示数据:
public interface Data {
String getContent();
}
因为会有两个Data实现类,一个是提货单FutureData,一个是蛋糕RealData,Data接口的作用是为了和客户端解耦,客户端只知道要的是Data,并不需要知道具体有多少种Data。
蛋糕RealData:
public class RealData implements Data{
private final String content;
public RealData(int count,char c) {
System.out.println("making RealData"+"("+count +","+c+")BEGIN");
char[] buffer = new char[count];
for (int i = 0; i < count; i++) {
buffer[i] = c;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("making RealData"+"("+count +","+c+")END");
content = new String(buffer);
}
@Override
public String getContent() {
return content;
}
}
这里只是进行一个耗时的简单字符数组赋值操作,这里用sleep故意产生耗时效果。
提货单FutureData:
public class FutureData implements Data{
private RealData realData = null;
private boolean ready = false;//realData是否准备好了的标志位
public synchronized void setRealData(RealData realData){
//如果已经准备好了,则直接返回
if (ready){
return;
}
//将真正的data赋值之后,将ready标志位置位
this.realData = realData;
ready = true;
notifyAll();
}
@Override
public synchronized String getContent() {
//如果realData还没有做好,则线程等待
if (!ready){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return realData.getContent();
}
}
当你拿着提货单要拿蛋糕的时候,店员说还没做好蛋糕(ready为false),你只好等待(wait),当店员做好蛋糕的时候,他叫你来拿蛋糕(notifyAll),这时你就可以拿回蛋糕(返回数据)。
专门负责开子线程的Host:
public class Host {
public Data request(final int count, final char c){
System.out.println("request"+"("+count +","+c+")BEGIN");
final FutureData futureData = new FutureData();
new Thread(){
@Override
public void run() {
RealData realData = new RealData(count,c);
futureData.setRealData(realData);
}
}.start();
return futureData;
}
}
开子线程生产RealData(耗时操作),等到RealData生产完成,就赋值到FutureData实例中。
注意到在request方法中,立刻返回的是FutureData.
客户端程序:
public class Main {
public static void main(String args[]){
System.out.println("main Begin");
Host host = new Host();
Data data1 = host.request(10,'a');
Data data2 = host.request(20,'b');
Data data3 = host.request(30,'c');
System.out.println("main do other job");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("data1=" + data1.getContent());
System.out.println("data2=" + data2.getContent());
System.out.println("data3=" + data3.getContent());
System.out.println("main end");
}
}
因为Host的request返回的是FutureData实例,所以并不能在返回之后马上拿到想要的数据,在一段时间做其他处理之后,
调用Data实例(这里是返回的FutureData)的getContent方法,在FutureData代码中可以看到,getContent会先判断
RealData是否已经被生产出来,如果还没有,则主线程还需在此等待,直到RealData已经产生。如果RealData已经被生产出来、
则立刻获取结果。
最后看下运行结果:
main Begin
request(10,a)BEGIN
request(20,b)BEGIN
request(30,c)BEGIN
main do other job
making RealData(30,c)BEGIN
making RealData(10,a)BEGIN
making RealData(20,b)BEGIN
making RealData(10,a)END
data1=aaaaaaaaaa
making RealData(20,b)END
data2=bbbbbbbbbbbbbbbbbbbb
making RealData(30,c)END
data3=cccccccccccccccccccccccccccccc
main end
只有在RealData创建出来之后,主线程才可以将RealData实例的content读取出来。