概述
通过对多线程的学习,我们知道创建线程有三种方式,分别是继承Thread类,实现Runnable接口,实现Callable接口。我们目前主要使用的都是使用实现Runnable接口创建线程,但是还是需要掌握使用Callable来创建线程。


由两者的代码比较可知,使用Runnable创建线程运行是没有返回值的,而Callable是由返回值的。我们查看两者的文档


发现Callable是可以运行抛出异常的,而Runnable是不返回结果,也不能抛出被检查的异常。
直接继承Thread,另外一种就是实现Runnable接口。这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
使用
实现了Runnable接口之后如何使用呢?照样查看Java官方文档

查看了Thread的所有构造方法,并没有发现与Callable相关的方法。那我们怎么使用呢?
一般情况下是配合ExecutorService
来使用的,也就是前面讲到的。这里我们讲的是另一种方式。首先查看Runnable文档

我们发现Runnable有一个子接口RunnableFuture
,既然RunnableFuture是Runnable的子接口所有我们可以传该接口,查看文档,

我们发现了RunnableFuture有一个实现类FutureTask
,点击查看文档

查看构造方法~找到了一个可以传入Callable的构造方法!!

也就是说我们可以通过FutureTask
接口来运行Callable。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ThreadDemo { public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<>(new Data());
new Thread(futureTask).start(); } }
class Data implements Callable<Integer>{
@Override public Integer call() throws Exception { System.out.println("Callable创建线程"); return 1024; } }
|

这里可以看见线程已经创建并启动成功了!但是如何获取到call方法到返回值呢??我们可以通过get()
方法获取


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class ThreadDemo { public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new Data());
new Thread(futureTask).start(); System.out.println(futureTask.get()); } }
class Data implements Callable<Integer>{
@Override public Integer call() throws Exception { System.out.println("Callable创建线程"); return 1024; } }
|
细节
当我们使用FutureTask.get()
获取到返回结果时,如果线程执行还没有结束,是会导致其他线程被阻塞的,因为我们需要一直等待获取的结果。(也有重载方法设置等待时间)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread()); new Thread(futureTask,"AA").start();
Integer result = futureTask.get(); System.out.println(Thread.currentThread().getName());
System.out.println("result:"+result);
} }
class MyThread implements Callable<Integer> {
@Override public Integer call() throws Exception { System.out.println(Thread.currentThread().getName()+"\tCome in Callable"); TimeUnit.SECONDS.sleep(3); return 1024; } }
|

所以我们通常被get()放到最后面。
一个任务不能被反复执行,如果想反复执行,必须重新设置新任务。

设置新任务~

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class CallableDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread()); FutureTask<Integer> futureTask2 = new FutureTask<>(new MyThread());
new Thread(futureTask,"AA").start(); new Thread(futureTask2,"BB").start();
Integer result = futureTask.get(); System.out.println(Thread.currentThread().getName());
System.out.println("result:"+result);
} }
class MyThread implements Callable<Integer> {
@Override public Integer call() throws Exception { System.out.println(Thread.currentThread().getName()+"\tCome in Callable"); TimeUnit.SECONDS.sleep(3); return 1024; } }
|