ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## **主线程如何得知异步线程的执行结果?** 通过继承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 ``` 完美