【高并发】2.8万字的Callable和Future面试知识点总结,看完我直接面进了字节跳动,原谅我有点飘了(中)
大家好,我是冰河~~
在Java的多线程编程中,除了Thread类和Runnable接口外,不得不说的就是Callable接口Future接口了。使用继承Thread类或者实现Runnable接口的线程,无法返回最终的执行结果数据,只能等待线程执行完成。此时,如果想要获取线程执行后的返回结果,那么,Callable和Future就派上用场了。
注:文章全程高能,建议收藏,如果文章对你有点帮助,小伙伴们一键三连呀,谢谢啦~~
两种异步模型与深度解析Future接口
两种异步模型
在Java的并发编程中,大体上会分为两种异步编程模型,一类是直接以异步的形式来并行运行其他的任务,不需要返回任务的结果数据。一类是以异步的形式运行其他任务,需要返回结果。
1.无返回结果的异步模型
无返回结果的异步任务,可以直接将任务丢进线程或线程池中运行,此时,无法直接获得任务的执行结果数据,一种方式是可以使用回调方法来获取任务的运行结果。
具体的方案是:定义一个回调接口,并在接口中定义接收任务结果数据的方法,具体逻辑在回调接口的实现类中完成。将回调接口与任务参数一同放进线程或线程池中运行,任务运行后调用接口方法,执行回调接口实现类中的逻辑来处理结果数据。这里,给出一个简单的示例供参考。
- 定义回调接口
package io.binghe.concurrent.lab04;
/**
* @author binghe
* @version 1.0.0
* @description 定义回调接口
*/
public interface TaskCallable<T> {
T callable(T t);
}
便于接口的通用型,这里为回调接口定义了泛型。
- 定义任务结果数据的封装类
package io.binghe.concurrent.lab04;
import java.io.Serializable;
/**
* @author binghe
* @version 1.0.0
* @description 任务执行结果
*/
public class TaskResult implements Serializable {
private static final long serialVersionUID = 8678277072402730062L;
/**
* 任务状态
*/
private Integer taskStatus;
/**
* 任务消息
*/
private String taskMessage;
/**
* 任务结果数据
*/
private String taskResult;
//省略getter和setter方法
@Override
public String toString() {
return "TaskResult{" +
"taskStatus=" + taskStatus +
", taskMessage='" + taskMessage + '\'' +
", taskResult='" + taskResult + '\'' +
'}';
}
}
- 创建回调接口的实现类
回调接口的实现类主要用来对任务的返回结果进行相应的业务处理,这里,为了方便演示,只是将结果数据返回。大家需要根据具体的业务场景来做相应的分析和处理。
package io.binghe.concurrent.lab04;
/**
* @author binghe
* @version 1.0.0
* @description 回调函数的实现类
*/
public class TaskHandler implements TaskCallable<TaskResult> {
@Override
public TaskResult callable(TaskResult taskResult) {
//TODO 拿到结果数据后进一步处理
System.out.println(taskResult.toString());
return taskResult;
}
}
- 创建任务的执行类
任务的执行类是具体执行任务的类,实现Runnable接口,在此类中定义一个回调接口类型的成员变量和一个String类型的任务参数(模拟任务的参数),并在构造方法中注入回调接口和任务参数。在run方法中执行任务,任务完成后将任务的结果数据封装成TaskResult对象,调用回调接口的方法将TaskResult对象传递到回调方法中。
package io.binghe.concurrent.lab04;
/**
* @author binghe
* @version 1.0.0
* @description 任务执行类
*/
public class TaskExecutor implements Runnable{
private TaskCallable<TaskResult> taskCallable;
private String taskParameter;
public TaskExecutor(TaskCallable<TaskResult> taskCallable, String taskParameter){
this.taskCallable = taskCallable;
this.taskParameter = taskParameter;
}
@Override
public void run() {
//TODO 一系列业务逻辑,将结果数据封装成TaskResult对象并返回
TaskResult result = new TaskResult();
result.setTaskStatus(1);
result.setTaskMessage(this.taskParameter);
result.setTaskResult("异步回调成功");
taskCallable.callable(result);
}
}
到这里,整个大的框架算是完成了,接下来,就是测试看能否获取到异步任务的结果了。
- 异步任务测试类
package io.binghe.concurrent.lab04;
/**
* @author binghe
* @version 1.0.0
* @description 测试回调
*/
public class TaskCallableTest {
public static void main(String[] args){
TaskCallable<TaskResult> taskCallable = new TaskHandler();
TaskExecutor taskExecutor = new TaskExecutor(taskCallable, "测试回调任务");
new Thread(taskExecutor).start();
}
}
在测试类中,使用Thread类创建一个新的线程,并启动线程运行任务。运行程序最终的接口数据如下所示。
TaskResult{taskStatus=1, taskMessage='测试回调任务', taskResult='异步回调成功'}
大家可以细细品味下这种获取异步结果的方式。这里,只是简单的使用了Thread类来创建并启动线程,也可以使用线程池的方式实现。大家可自行实现以线程池的方式通过回调接口获取异步结果。
2.有返回结果的异步模型
尽管使用回调接口能够获取异步任务的结果,但是这种方式使用起来略显复杂。在JDK中提供了可以直接返回异步结果的处理方案。最常用的就是使用Future接口或者其实现类FutureTask来接收任务的返回结果。
- 使用Future接口获取异步结果
使用Future接口往往配合线程池来获取异步执行结果,如下所示。
package io.binghe.concurrent.lab04;
import java.util.concurrent.*;
/**
* @author binghe
* @version 1.0.0
* @description 测试Future获取异步结果
*/
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "测试Future获取异步结果";
}
});
System.out.println(future.get());
executorService.shutdown();
}
}
运行结果如下所示。
测试Future获取异步结果
- 使用FutureTask类获取异步结果
FutureTask类既可以结合Thread类使用也可以结合线程池使用,接下来,就看下这两种使用方式。
结合Thread类的使用示例如下所示。
package io.binghe.concurrent.lab04;
import java.util.concurrent.*;
/**
* @author binghe
* @version 1.0.0
* @description 测试FutureTask获取异步结果
*/
public class FutureTaskTest {
public static void main(String[] args)throws ExecutionException, InterruptedException{
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
return "测试FutureTask获取异步结果";
}
});
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}
运行结果如下所示。
测试FutureTask获取异步结果
结合线程池的使用示例如下。
package io.binghe.concurrent.lab04;
import java.util.concurrent.*;
/**
* @author binghe
* @version 1.0.0
* @description 测试FutureTask获取异步结果
*/
public class FutureTaskTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
return "测试FutureTask获取异步结果";
}
});
executorService.execute(futureTask);
System.out.println(futureTask.get());
executorService.shutdown();
}
}
运行结果如下所示。
测试FutureTask获取异步结果
可以看到使用Future接口或者FutureTask类来获取异步结果比使用回调接口获取异步结果简单多了。注意:实现异步的方式很多,这里只是用多线程举例。
接下来,就深入分析下Future接口。
- 点赞
- 收藏
- 关注作者
评论(0)