了解线程池之前需要对线程定义的方法进一步补充,可以参见《JAVA多线程基础篇 2、如何创建线程》
1. Callable
在JDK5之后,增加了Callable
接口,实现该接口的类,可以类似Runnable
方法,在多线程环境中运行,并且返回运行结果。
public class CallableA implements Callable<String>{
@Override
public String call() throws Exception {
//模拟耗时计算
Thread.sleep(1000);
return "您好 ! 蛋糕做好了";
}
}
2. Future与线程池配合运行
调用Callable对象,需要配合Future和线程池。
线程池提交Callable任务后,会返回Future对象.此时Future对象只是一个占位符,就好像去订蛋糕,蛋糕店会给你一张提货小票。
当调用future.get()方法时,才会阻塞直到获取结果。
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit(new CallableA());
System.out.println(future.get());
executorService.shutdown();
}
运行结果:
您好 ! 蛋糕做好了
3. FutureTask
FutureTask非常灵活。它同时实现了Runnable接口和Callable接口,,还实现了Future接口。所以FutureTask既能当做一个Runnable直接被Thread执行,也能作为Future用来得到Callable的计算结果。
3.1 FutureTask作为线程单独运行
public class FutureTaskTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> makecake = new Callable<String>(){
@Override
public String call() throws Exception {
return "蛋糕做好了";
}
};
FutureTask<String> task = new FutureTask(makecake);
new Thread(task).start();
System.out.println(task.get());
}
}
运行结果:
您好 ! 蛋糕做好了
3.2 FutureTask与线程池配合运行
也可以将FutureTask提交到线程池运行,然后从FutureTask里取出Future结果。
因为FutureTask即实现了Runnable接口和Callable,还实现了Future接口。
public class FutureTaskTest2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> makecake = new Callable<String>(){
@Override
public String call() throws Exception {
return "蛋糕做好了";
}
};
FutureTask<String> task = new FutureTask(makecake);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(task);
System.out.println(task.get());
}
}
运行结果:
您好 ! 蛋糕做好了
总结
除了Runnable接口外,Callable结合线程池、以及FutureTask类都可以提交线程任务。
多线程系列在github上有一个开源项目,主要是本系列博客的实验代码。
https://github.com/forestnlp/concurrentlab
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。