八股代码实测:runable和callable
Callable VS Runnable对比 | 南瓜慢说知识库
java中接口Callable的用法_java callable-优快云博客
Callable<String>
是一个函数式接口,它的作用是返回一个 String 类型的结果(不需要任何传参),其对应的形式可能就是这样定义: () -> "hello, world";
当然这是一个Lambda表达式,我们也可以在其内写复杂的业务逻辑,与Runable不同的是Callable还可以抛出异常,最终返回一个String类型的结果即可:
Callable<String> callable = () -> {
if(Math.random() > 0.5) {
return "hello, world";
} else {
throw new Exception("error");
}
};
我们在Callable中定义具体的任务之后,接下来模拟多线程并发场景,来看看它是如何执行的:
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Callable<String>> tasks = new ArrayList<>();
for(int i = 0; i < 10; i++) {
Callable<String> callable = () -> "task " + i;
tasks.add(callable);
}
List<Future<String>> results = executorService.invokeAll(tasks);
for(Future<String> future : results) {
System.out.println(future.get());
}
executorService.shutdown();
这里用一个List 来存放多个任务,例如这里单个任务就是打印内容,之后引入Future对象(异步操作),Future对象通常是通过Executor框架提交的异步任务返回的,可以使用Future.get()方法来获取异步任务的执行结果。如果异步任务尚未完成,则调用Future.get()方法将会阻塞线程,直到异步任务执行完毕并返回结果。
流程:
- 创建Future对象(通过Executor框架的submit()方法来创建)
- 获取异步任务结果(通过Future对象的get()方法)
- 可引入try…catch,使用get()捕获异常
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Callable<String>> tasks = new ArrayList<>();
for(int i = 0; i < 10; i++) {
Callable<String> callable = () -> "task " + i;
tasks.add(callable);
}
List<Future<String>> results = executorService.invokeAll(tasks);
for(Future<String> future : results) {
System.out.println(future.get());
}
executorService.shutdown();
什么时候用Callable<String>
?
通常是用于多线程并发的时候,一个常见的场景是引入线程池操作的时候,可以在当前线程执行业务逻辑的时候
继承Callable,需要重写call方法
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
其中V代表最后调用call方法,返回的数据类型,它可以是一个类。在重写call方法中,通常处理逻辑是查询数据库数据,将一个对象进行初始化并填充返回
传入的参数是用来生成缓存的 key,也就是说,它的作用是动态生成缓存的键。在调用 callCacheKey.call() 时,会执行传入的 Callable 对象中的 call 方法,返回一个缓存的 key。
首先科普下 Callable<String>
,简单来说就是一个请求进来,如果你使用了DeferredResult或者Callable,在没有得到返回数据之前,DispatcherServlet和所有Filter就会退出Servlet容器线程,但响应保持打开状态,一旦返回数据有了,这个DispatcherServlet就会被再次调用并且处理,以异步产生的方式,向请求端返回值。
这么做的好处就是请求不会长时间占用服务连接池,提高服务器的吞吐量。
本质上就类似于引入了一个消息队列,在拿到返回数据就可以将数据作为消息的载荷异步发送给消息队列中,再由消息队列传回客户端,当然这种形式是
@GetMapping("/callable")
public Callable<String> testCallable() throws InterruptedException {
log.info("主线程开始!");
Callable<String> result = new Callable<String>() {
@Override
public String call() throws Exception {
log.info("副线程开始!");
Thread.sleep(1000);
log.info("副线程结束!");
return "SUCCESS";
}
};
log.info("主线程结束!");
return result;
}