概述

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

code-snapshot (8)

img

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

image-20200927164920768

image-20200927164948891

发现Callable是可以运行抛出异常的,而Runnable是不返回结果,也不能抛出被检查的异常。

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

使用

实现了Runnable接口之后如何使用呢?照样查看Java官方文档

image-20200927165939590

查看了Thread的所有构造方法,并没有发现与Callable相关的方法。那我们怎么使用呢?

一般情况下是配合ExecutorService来使用的,也就是前面讲到的。这里我们讲的是另一种方式。首先查看Runnable文档

image-20200927170605135

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

image-20200927170654991

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

image-20200927170912307

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

image-20200927170935666

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

image-20200927171313646

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;
}
}

image-20200927171336724

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

image-20200927171801977

image-20200927171632474

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();
//获取call方法的返回值
System.out.println(futureTask.get());
}
}


class Data implements Callable<Integer>{

@Override
public Integer call() throws Exception {
System.out.println("Callable创建线程");
return 1024;
}
}

细节

当我们使用FutureTask.get()获取到返回结果时,如果线程执行还没有结束,是会导致其他线程被阻塞的,因为我们需要一直等待获取的结果。(也有重载方法设置等待时间)

image-20200927173900703

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 {
//两个线程,一个main主线程,一个是AA-futureTask线程

//public FutureTask(Callable<V> callable)
//创建一个 FutureTask ,它将在运行时执行给定的 Callable 。
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
new Thread(futureTask,"AA").start();

//建议放在后面写
Integer result = futureTask.get();//要求获得Callable线程的计算结果,如果没有计算完成就要去强求,会导致堵塞,直到计算完成
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;
}
}

image-20200927173936823

所以我们通常被get()放到最后面。

一个任务不能被反复执行,如果想反复执行,必须重新设置新任务。

image-20200927174302431

设置新任务~

image-20200927174341743

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 {
//两个线程,一个main主线程,一个是AA-futureTask线程

//public FutureTask(Callable<V> callable)
//创建一个 FutureTask ,它将在运行时执行给定的 Callable 。
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();//要求获得Callable线程的计算结果,如果没有计算完成就要去强求,会导致堵塞,直到计算完成
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;
}
}