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。
以上为个人理解,如有不对请拍砖,谢谢~
推荐阅读
-
Java的Thread,Runnable、Callable、Future、FutureTask、Executors,ExecutorService
-
Java多线程下的其他组件之CyclicBarrier、Callable、Future和FutureTask详解
-
Java 继承Thread 实现Runnable接口和实现Callable接口创建线程的区别
-
Java并发编程 - Executor,Executors,ExecutorService, CompletionServie,Future,Callable
-
Java的Thread,Runnable、Callable、Future、FutureTask、Executors,ExecutorService
-
Java多线程下的其他组件之CyclicBarrier、Callable、Future和FutureTask详解
-
Java 继承Thread 实现Runnable接口和实现Callable接口创建线程的区别