## **主线程如何得知异步线程的执行结果?**
通过继承Thread类或者实现Runnable接口方式创建的线程在执行后无法获取执行结果,除非采用共享变量或者线程通信的方式获得,但显然这不是一种优雅的方式。Java中提供了使用Callable和Future来实现获取任务结果的操作,Callable用来执行任务,产生结果,而Future用来获得结果。
测试代码
```
// 通过实现Callable接口创建线程任务
class Task implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("异步线程正在执行任务...");
Thread.sleep(3000);
return "3000";
}
}
class test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程池
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(new Task());
System.out.println("主线程继续执行");
// future获取线程返回结果
String result = future.get();
System.out.println("主线程得到异步线程返回结果:" + result);
executor.shutdown();
}
}
```
运行结果
```
主线程继续执行
异步线程正在执行任务...
异步线程返回结果:3000
```
## **如何实现Future功能**
我们来模拟一个在主线程中进行远程**异步**请求并且**得到请求响应的数据**的功能
* 异步:那自然是需要在主线程中开启一个线程
* 得到异步响应的数据:就是我们下面要探讨实践的
1.创建远程请求的抽象类
```
public abstract class BaseRequest {
// 获取请求结果
public abstract String getResponse();
}
```
2.创建实现具体请求的类
```
public class SendRequest extends BaseRequest {
// 最终返回的值
private String result;
// 构造函数模拟发送耗时3秒的远程请求
public SendRequest(String params) {
System.out.println("正在读取数据...");
...
Thread.sleep(3000);
...
System.out.println("读取数据完成...");
result = "hello";
}
@Override
public String getResponse() {
return result;
}
}
```
3.创建请求future类,目前来看这个类只是对SendRequest的简单封装,暂且没多大意义。
```
public class FutureRequest extends BaseRequest {
private SendRequest sendRequest;
public void sendRequest(SendRequest sendRequest) {
this.sendRequest = sendRequest;
}
@Override
public String getResponse() {
return sendRequest.getResponse();
}
}
```
4.测试
```
class test {
public static void main(String[] args) {
FutureRequest futureRequest = new FutureRequest();
new Thread(new Runnable() {
@Override
public void run() {
SendRequest sendRequest = new SendRequest("我是请求参数");
futureRequest.sendRequest(sendRequest);
}
}).start();
System.out.println("主线程继续执行");
String result = futureRequest.getResponse();
System.out.println("主线程获得异步请求结果:" + result);
}
}
```
执行结果
```
Exception in thread "main" java.lang.NullPointerException
at com.mask.FutureRequest.getResponse(FutureRequest.java:23)
at com.mask.test.main(ThreadDemo.java:98)
正在读取数据...
读取数据完成...
```
很显然在获取结果的时候报了空指针的异常,SendRequest 构造函数sleep了3秒,主线程继续执行时FutureRequest 里的sendRequest对象还未创建成功,换种说法,主线程尝试去获取异步远程请求的结果,但此时远程请求还未得到响应,自然是获取不得。那可如何是好呢?继续改造代码
改造第3步的FutureRequest类中方法,加入wait、notify方法
```
public class FutureRequest extends BaseRequest {
private SendRequest sendRequest;
// notify和wait 方法必须在 synchronized 中使用
public synchronized void sendRequest(SendRequest sendRequest) {
this.sendRequest = sendRequest;
notify();
}
@Override
public synchronized String getResponse() {
// 如果请求还没响应时,主线程尝试获取结果会一直处于等待的状态,
// 直到请求成功响应发送notify通知,等待才会解除
while (sendRequest == null) {
...
wait();
...
}
return sendRequest.getResponse();
}
}
```
运行结果
```
主线程继续执行
异步任务正在读取数据...
异步任务读取数据完成...
主线程获得异步请求结果:hello
```
执行结果正是我们预期的结果,测试代码中主线程创建了线程去执行异步任务,但似乎代码看起来并不是那么的优雅,理想的future客户端应该是包含异步和获取异步结果,所以我们继续优化代码再封装一个future客户端供主线程使用。
```
// Future客户端
public class FutureClient {
public BaseRequest sendRequest(String params) {
FutureRequest futureRequest = new FutureRequest();
new Thread(new Runnable() {
@Override
public void run() {
SendRequest sendRequest = new SendRequest(params);
futureRequest.sendRequest(sendRequest);
}
}).start();
return futureRequest;
}
}
```
测试
```
class test {
public static void main(String[] args) {
FutureClient futureClient = new FutureClient();
BaseRequest baseRequest = futureClient.sendRequest("我是请求参数");
System.out.println("主线程继续执行");
String result = baseRequest.getResponse();
System.out.println("主线程获得异步请求结果:" + result);
}
}
```
执行结果
```
主线程继续执行
异步任务正在读取数据...
异步任务读取数据完成...
主线程获得异步请求结果:hello
```
完美