欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java的Thread,Runnable、Callable、Future、FutureTask、Executors,ExecutorService

程序员文章站 2022-07-12 18:47:44
...

定义解释 

 

Runnable:接口,线程任务的接口定义,一个run方法,无返回值,创建的线程无法获取返回值。

Callable<T>:接口,线程任务的接口定义,一个call方法,有返回值,返回值类型为指定的泛型。

Future<T>:接口,是Callable、Runnable(FutureTask可以将Runnable转换为Callable类型)的调度容器,它可以对执行的线程进行取消,查询是否完成,获取结果等操作,get方法获取结果会阻塞,直到任务返回结果。

FutureTask<T>:类,实现RunnableFuture接口的类,RunnableFuture继承了Runnable、Future<T>,所以相当于是实现类Runnable、Callable<T>接口的一个实现类,相当于具有Runnable和Future的功能。

Thread:类,实现Runnable接口的类,所以具有Runnable的特性,它的功能还有包装线程任务,作为异步任务的执行者,管理异步任务。

Executors:类,创建各种线程池,还有将Runnable转换为Callable的方法等。

ExecutorService:接口,各种类型线程池的接口定义。

 

所以创建线程有三种方式:

1、实现Runnable接口,交给Thread实例或者线程池去执行,无法获取任务执行结果。
2、Thread实现了Runnable接口,所以可以继承Thread类,直接执行或者交给线程池去执行,无法获取任务执行结果。
3、实现Callable<T>接口,并使用FutureTask<T>进行包装,交给Thread实例或者线程池去执行,可以获取到任务执行结果,但是使用get获取任务执行结果时会阻塞,直到任务返回结果。
FutureTask可以包装一个Runnable实例,实质上是将Runnable转换成了一个Callable,并赋予默认返回值。
//FutureTask的一个构造方法:
public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
}
 
//Executors的callable方法,返回一个RunnableAdapter实例:
public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
}
 
//RunnableAdapter是Executors的静态内部类,实现Callable接口
static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
}
来看下线程的实现方法: 
方法一(TaskA实现Runnable接口):
Thread thread = new Thread(new TaskA());
thread.start();
 
方法二(Task继承Thread类):
TaskB task = new TaskB();
task.start();
 方法三(实现Callable接口结合FutureTask):
TaskC task = new TaskC();
//带有返回值的线程
FutureTask<Integer> future = new FutureTask<Integer>(task);
Thread thread = new Thread(future);
thread.start();
System.out.println("获取Callable任务的返回值:" + future.get());
//FutureTask也可以运行Runnable任务,FutureTask会在构造方法里面将Runnable转换为Callable, 指定的返回值为 线程的返回值
FutureTask<Integer> future1 = new FutureTask<Integer>(new TaskA(), 2);
thread = new Thread(future1);
thread.start();
System.out.println("获取Runnable任务的返回值:" + future1.get());
 补充一个使用线程池调度的方式:
//创建一个同时只能执行一个任务的线程池
ExecutorService service = Executors.newSingleThreadExecutor();
TaskA taskA = new TaskA();
TaskB taskB = new TaskB();
//execute只能执行Runnable类型的任务,没有返回值
service.execute(taskA);
service.execute(taskB);
//submit执行Runnable类型的任务,返回值为null
Future<?> future = service.submit(taskA);
System.out.println("获取Runnable任务的返回值:" + future.get());
future = service.submit(taskA,2);
System.out.println("获取Runnable任务的返回值:" + future.get());
TaskC taskCallable = new TaskC();
future = service.submit(taskCallable);
System.out.println("获取Callable任务的返回值:" + future.get());
 
最后比较以下三种方式的特点:
1、Runnable方式:只提供任务实例, 需要依赖任务调度器执行,无返回值。
2、Thread方式:实现了Runnable接口,可以包装任务实例,并作为任务调度器直接执行,无返回值。也可以说Thread是间接地通过实现Runnable接口才具备了创建线程任务的功能,哈哈。但是由于Thread可以作为线程任务调度执行器,所以他具有线程管理的功能,比如join,interrupt等。
3、Callable方式:跟Runnable相似,但可以获取线程执行的返回值,但是获取返回值时会阻塞,直到线程任务返回。
所以只创建任务的话,一般用Runnable,多个线程之间进行通信交互时,用Thread,需要获取返回值时,用Callable
以上为个人理解,如有不对请拍砖,谢谢~