Java并发编程(十八)Future模式

Future模式

Future模式是在多线程程序中设计中的一个非常常见的设计模式,它和Runnable非常类似,总整体而言,可以看作为有了返回值的Runnable。但是Future模式是继承Callable接口,重写call()方法的:

1
2
3
4
5
6
7
8
import java.util.concurrent.Callable;

public class test implements Callable {
@Override
public Object call() throws Exception {
return null;
}
}

Future模式的由来

Future模式的诞生也很贴近现实,我们的Runnable接口模式可以看做为很古老的 “一手交钱一手交货”,而Future模式则看作为,我们使用支票交易。这两者的区别在于哪里呢?Runnable要求你来交易的时候,必须去银行把钱全都取出来后,才可以交易,比如你运行的run方法,要工作完了,才可以交易,而Future模式则表示,我告诉你我能够给你钱,到时候你去银行取就可以了,我们先进行交易。这样的改进极大的提高了效率。

future

就如上图所示main代表主系统,client表示客户要处理的信息,data表示客户最终要获得的信息,realdata表示实际的数据,而使用future可以先返回一个futuredata,给data,futuredata是realdata包装,futuredata先返回结果值到data中,然后realdata再慢慢返回全部的数据。这种模式可以应用到大规模的抽奖当中,抽奖的时候可以先返回你是否抽到奖,之后再慢慢返回抽到的奖品的具体信息。

实现Future模式

我们可以试着手写出这样的模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public interface Data {
public String getResult();
}
//
public class FutureData implements Data {
protected RealData realData=null;
protected boolean isReady=false;
public synchronized void setRealData(RealData realData){
if (isReady){
return;
}
this.realData=realData;
isReady=true;
System.out.println("真实数据到达,唤醒getResult");
notifyAll();
//当真实的数据已经到达之后,唤醒全部的线程
}
@Override
public synchronized String getResult() {
while (!isReady){
try {
System.out.println("等待真实数据处理完毕");
wait();
//等到真实的处理处理完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return realData.result;
}
}
//
public class RealData implements Data {
protected final String result;

public RealData(String result) {
StringBuffer sb=new StringBuffer();
for (int i = 0; i <10 ; i++) {
sb.append(result);
try {
Thread.sleep(100);
//模拟线程真实数据的缓慢处理
} catch (InterruptedException e) {
e.printStackTrace();
}
}

this.result = sb.toString();
//完成构造
}

@Override
public String getResult() {
return null;
}
}
//
public class Client {
public Data request (final String q){
final FutureData future=new FutureData();
new Thread(){
@Override
public void run(){
//真实数据构建比较慢,所以在单独的线程中运行
System.out.println("开始对真实数据进行搭建");
RealData realData=new RealData(q);
future.setRealData(realData);
}
}.start();
System.out.println("返回future");
return future;
}
}
//

public class test {
public static void main(String[] args) {
Client client=new Client();
//这里将立即返回,因为得到的是future凭证,而不是真实数据
Data data=client.request("qwe");
System.out.println("请求完毕");
try {
//模拟其他业务的进行,不妨碍真实数据
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//使用真实的数据
System.out.println("getResult:数据="+data.getResult());
}

}

返回future
请求完毕
开始对真实数据进行搭建
真实数据到达,唤醒getResult
getResult:数据=qweqweqweqweqweqweqweqweqweqwe

可以看到,是先返回一个凭证,然后让程序接着运行下去,最后获得结果。

JDK内置的Future模式

这种Future模式在JDK当然内置也有,如图:

fu2

我们使用内置的Future模式来试试吧,它主要是继承一个Callable接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.concurrent.Callable;

public class RealData implements Callable {
private String para;

public RealData(String para) {
this.para = para;
}

@Override
public Object call() throws Exception {
StringBuffer sb=new StringBuffer();
System.out.println("开始搭建真实数据");
for (int i = 0; i <10 ; i++) {
sb.append(para);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("返回数据");
return sb.toString();
}
}

然后再测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//构造FutureTask
FutureTask<String> f=new FutureTask<String>(new RealData("qwe"));
//这里会传入一个值表示使用真实的数据,而我们的真实数据会先返回Future凭证
ExecutorService es= Executors.newFixedThreadPool(1);
es.submit(f);
System.out.println("请求完毕");
try {
System.out.println("做些其他的事情");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据="+f.get());
es.shutdown();
}
}

请求完毕
做些其他的事情
开始搭建真实数据
返回数据
数据=qweqweqweqweqweqweqweqweqweqwe

注意:这里和run方法的区别是,如果你使用Runable接口去实现,那么在run方法执行完之后,数据就没了,或者直接输出,但是,这样是不会被自己给控制的。这里使用的future,想让它什么时候输出数据,就什么时候输出数据。