hello, 大家好,又见面了。
通过前面几篇文章,我相信大家应该对于如何使用多线程执行任务应该都有了一定的了解。今天我们来讲一讲Callable和Future。
我们之前的线程任务都是使用的Runnable接口,这个任务最终的体现是写在里面 run方法中的内容,大家要这个run() 方法是没有返回值的,也就代表这我们使用Runnable 作为线程任务的时候是没有返回值的。
那比如我又一个需求,需要开启一个新的线程去统计一个结果,那这个线程执行结束之后,是需要把统计出来的结果告诉我的,很遗憾, Runnable是做不到的,他做多能帮我们打印到控制台上,但是并没有什么用。这也就引出了Callable的作用:
Callable: 也是一个接口,用来描述线程任务,但是他是一个有返回值的线程任务。
所以以后我们在开发线程任务的时候就用两种选择,一种是Runnable代表没有返回值的线程任务;一种是Callable代表有返回值的线程任务。
同时Callable是可以抛出异常的,而Runnable无法抛出,只能在方法中通过try..catch处理。
那我们先来了解一下Callable的用法。callable接口中只有一个方法,叫做call, 它有返回值。这个方法适合Runnable中的run方法想对应的。
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
那么我们应该如何执行Callable呢?看一段代码:
package com.lsqingfeng.action.knowledge.multithread.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
/**
* @className: CallableDemo
* @description: Callable案例
* @author: sh.Liu
* @date: 2022-04-08 15:03
*/
public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 创建一个有返回值的线程任务Callable, 返回String
Callable<String> c = new Callable(){
@Override
public Object call() throws Exception {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "| 正在执行有返回值任务");
return "success";
}
};
// 2. 创建一个Future用来执行多线程任务,构造方法中传入Callable类型变量
FutureTask<String> futureTask = new FutureTask<>(c);
// 创建Thread 线程,传入futureTask
Thread t1 = new Thread(futureTask);
// 3. 使用futureTask 执行线程任务
t1.start();
// 4. 获取该任务的返回结果:
System.out.println(futureTask.get());
}
}
打印结果:
Thread-0| 正在执行有返回值任务 success
执行结果在两秒后打印出来,也证明了 get方法是个阻塞的方法。
除了上面的方式以外,我们仍然可以使用线程池来运行有返回值的线程任务-Callable。
package com.lsqingfeng.action.knowledge.multithread.callable;
import java.util.concurrent.*;
/**
* @className: CallableDemo2
* @description: Callable案例: 使用线程池执行Callable
* @author: sh.Liu
* @date: 2022-04-08 16:35
*/
public class CallableDemo2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 2. 创建一个Callable 线程任务
Callable<Integer> c1 = ()->{
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "| 开始执行任务----");
return 1;
};
// 3. 执行线程任务,获取结果
Future<Integer> future = executorService.submit(c1);
// 4.打印结果
System.out.println(future.get());
}
}
如果说我们现在有一个Runnable类型的任务,也想在执行完任务的时候,给调用者返回一个结果。这个时候怎么办呢?Java中也未我们提供了相关的API:
这个问题归根接地是怎么把一个Runnable转为Callable的问题。
package com.lsqingfeng.action.knowledge.multithread.callable;
import java.util.concurrent.*;
/**
* @className: CallableDemo3
* @description:
* @author: sh.Liu
* @date: 2022-04-08 17:05
*/
public class CallableDemo3 {
public static void main(String[] args) {
Runnable r1 = ()->{
System.out.println("I am Runnable");
};
// 转换成Callable:
Callable<String> c1 = Executors.callable(r1, "result");
// 然后运行Callable的方式即可:
// 方式二: 使用FutureTask的方式,传入Runnable和结果
FutureTask futureTask = new FutureTask(r1, "result2");
new Thread((futureTask)).start();
// 方式三: 使用线程池, 提交的时候传入Runnable和result
ExecutorService executorService = Executors.newFixedThreadPool(3);
Future<String> result2 = executorService.submit(r1, "result2");
}
}
好了,关于Callable的内容我们就介绍到这里了。整体来说用法还是比较简单的。就记住有返回值的多线程就是用Callable。如果对你有帮助,记得点点关注。下篇文章我们聊点别的。