【图解Java多线程设计模式】Future模式

获取Future角色的线程会在稍后使用Future角色来获取运行结果。如果运行结果已经出来了,那么直接领取即可;如果运行结果还没有出来,那么需要等待结果出来。

示例

在每次发出请求时都创建一个线程。程序一旦发出请求,就会立即获取返回值。也就是说,会有下面这样的返回值(data)。

Data data = host.request(10, ‘A’);

但是,这里的返回值data并非请求的运行结果。为了获取请求的运行结果,刚刚启动了其他线程去进行计算。

如下所示,过了一段时间后,线程会调用data的getContent方法去获取运行结果。

data.getContent

如果其他线程处理完了请求,那么调用getContent的线程会立即从该方法返回;而如果其他线程还没有处理完请求,那么调用getContent的线程会继续等待运行结果。

类图

kMRQOJ.png

时序图

kMR8T1.png

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 otherJob BEGIN");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("main otherJob END");

System.out.println("data1 = " + data1.getContent());
System.out.println("data2 = " + data2.getContent());
System.out.println("data3 = " + data3.getContent());

System.out.println("main END");
}
}

Host.java

执行request的线程会做以下三件事情。

  • 创建FutureData的实例
  • 启动一个新线程,用于创建RealData的实例
  • 将FutureData的实例作为返回值返回给调用者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Host {
public Data request(final int count, final char c) {
System.out.println(" request(" + count + ", " + c + ") BEGIN");

final FutureData future = new FutureData();

new Thread() {
@Override
public void run() {
RealData realdata = new RealData(count, c);
future.setRealData(realdata);
}
}.start();

System.out.println(" request(" + count + ", " + c + ") END");
return future;
}
}

Data.java

1
2
3
public interface Data {
public abstract String getContent();
}

FutureData.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class FutureData implements Data {
private boolean ready = false;
private RealData realdata = null;

public synchronized void setRealData(RealData realdata) {
if (ready)
return;

this.ready = true;
this.realdata = realdata;
notifyAll();
}

@Override
public synchronized String getContent() {
while(!ready) {
try {
wait();
} catch (InterruptedException e) {
}
}
return realdata.getContent();
}
}

RealData.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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) {
}
}
this.content = new String(buffer);

System.out.println(" making RealData(" + count + ", " + c + ") END");
}

@Override
public String getContent() {
return content;
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
main BEGIN
request(10, A) BEGIN
request(10, A) END
request(20, B) BEGIN
request(20, B) END
request(30, C) BEGIN
request(30, C) END
main otherJob BEGIN
making RealData(30, C) BEGIN
making RealData(10, A) BEGIN
making RealData(20, B) BEGIN
making RealData(10, A) END
main otherJob END
data1 = AAAAAAAAAA
making RealData(20, B) END
data2 = BBBBBBBBBBBBBBBBBBBB
making RealData(30, C) END
data3 = CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
main END

登场角色

Client(请求者)

Client角色向Host角色发出请求(request),并会立即接收到请求的处理结果(返回值)——VirtualData角色。
不过,这里接收到的VirtualData角色实际上是Future角色。Client角色没有必要知道返回值究竟是RealData角色还是Future角色。稍后,Client角色会通过VirtualData角色来进行操作。
在示例程序中,由Main类扮演此角色。

Host

Host角色会创建新的线程,并开始在新线程中创建RealData角色。同时,它会将Future角色(当作VirtualData角色)返回给Client角色。在示例程序中,由Host类扮演此角色。
新线程在创建了RealData角色后,会将其设置到Future角色中。

VirtualData(虚拟数据)

VirtualData角色是让Future角色与RealData角色具有一致性的角色。在示例程序中,由Data接口扮演此角色。

RealData(真实数据)

RealData角色是表示真实数据的角色。创建该对象需要花费很多时间。在示例程序中,由RealData类扮演此角色。

Future(期货)

Future角色由Host角色传递给Client角色。从程序行为上看,对Client角色而言,Future角色就是VirtualData角色。实际上,当Client角色操作Future角色时,线程会调用wait方法等待,直至RealData角色创建完成。但是,一旦RealData角色创建完成,线程就不会再继续等待。Future角色会将Client角色的操作委托给RealData角色。
在示例程序中,由FutureData类扮演此角色。

类图

kQO0UK.png