概述
通过对多线程的学习,我们知道创建线程有三种方式,分别是继承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。
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()
方法获取
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()
获取到返回结果时,如果线程执行还没有结束,是会导致其他线程被阻塞的,因为我们需要一直等待获取的结果。(也有重载方法设置等待时间)
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;
}
}
所以我们通常被get()放到最后面。
一个任务不能被反复执行,如果想反复执行,必须重新设置新任务。
设置新任务~
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;
}
}
网友评论