创建线程的两种方式
一种是实现Runnable接口,实现其run方式;另外一种是继承Thread类,重写其run方法。
这一点可以从Thread类中的注释看到:
Runnable
Thread类也实现了Runnable接口,Runnable接口的源码如下:
public static CompletableFuture supplyAsync(Supplier supplier) public static CompletableFuture supplyAsync(Supplier supplier,Executor executor); public static CompletableFuture runAsync(Runnable runnable); public static CompletableFuture runAsync(Runnable runnable,Executor executor);
因此,可以看得出,不管我们是实现Runnable接口,还是继承Thread类,run方式的返回类型都是void,我们都无法拿到线程运行的返回结果。
如果非要拿到线程运行的结果怎么办?Callable便应运而生了。
Callable
@FunctionalInterfacepublic interface Callable { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception;}
Callable还是一个函数式接口,其泛型参数类型与返回值类型一致。
为了更清晰的说明他们之间的关系,这里先放上类图结构
类图关系
Future
Callable接口,只是用于单纯地去获得返回值,但是什么时候去获取,或者查看线程运行的状态,就需要借助Future
Future的源码如下:
public CompletableFuture whenComplete(BiConsumer super T, ? super Throwable> action); public CompletableFuture whenCompleteAsync(BiConsumer super T, ? super Throwable> action); public CompletableFuture whenCompleteAsync(BiConsumer super T, ? super Throwable> action, Executor executor); public CompletableFuture exceptionally(Function fn);
可以看得出来,Future提供了取消任务、获取任务执行的状态与获取结果的能力。
RunnableFuture
public interface RunnableFuture extends Runnable, Future { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run();}
RunnableFuture继承了Runnable与Future接口,没什么好说的。
以上说的,Runnable、Callable、Future以及RunnableFuture都是接口,真正干活还得靠FutureTask
FutureTask
FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable与Future接口,因此FutureTask既可以作为一个Runnable被Thread执行,也可以获取到Future异步计算的结果。
从上面的类图可以看到,传入一个Callable类型的参数可以构造FutureTask对象。
下面这个例子,使用构造出来的FutureTask对象,传入Thread的构造函数中,调用start方法以启动线程。
package com.qcy.testFutureTask;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;/** * &#64;author qcy * &#64;create 2020/09/07 09:27:06 */public class Main { static class Task implements Callable { &#64;Override public Integer call() throws Exception { //模拟任务耗时 Thread.sleep(2000); return 1; } } public static void main(String[] args) throws ExecutionException, InterruptedException { //FutureTask接收一个Callable实现类 FutureTask futureTask &#61; new FutureTask<>(new Task()); //FutureTask实现了RunnableFuture,间接实现了Runnable接口,因此可作为参数传给Thread的构造方法中 new Thread(futureTask).start(); //get()方法会阻塞2秒,输出1 int result &#61; futureTask.get(); System.out.println(result); }}
当然也可以将构造出来FutureTask对象&#xff0c;提交进线程池中。对线程池不了解的同学&#xff0c;可以移步这一篇文章说说线程池
package com.qcy.testFutureTask;import java.util.concurrent.*;/** * &#64;author qcy * &#64;create 2020/09/07 09:27:06 */public class Main { static class Task implements Callable { &#64;Override public Integer call() throws Exception { //模拟任务耗时 Thread.sleep(2000); return 1; } } public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService pool &#61; Executors.newFixedThreadPool(1); //FutureTask接收一个Callable实现类 FutureTask futureTask &#61; new FutureTask<>(new Task()); //这里使用一个Runnable的实现类&#xff0c;因此可以使用FutureTask pool.submit(futureTask); //get()方法会阻塞2秒,输出1 int result &#61; futureTask.get(); System.out.println(result); pool.shutdown(); }}
当然&#xff0c;ExecutorService内的submit方法&#xff0c;可以直接传入Callable的实现类。
package com.qcy.testFutureTask;import java.util.concurrent.*;/** * &#64;author qcy * &#64;create 2020/09/07 09:27:06 */public class Main { static class Task implements Callable { &#64;Override public Integer call() throws Exception { //模拟任务耗时 Thread.sleep(2000); return 1; } } public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService pool &#61; Executors.newFixedThreadPool(1); //这里使用一个Callable的实现类&#xff0c;因此可以使用FutureTask Future future &#61; pool.submit(new Task()); //get()方法会阻塞2秒,输出1 int result &#61; future.get(); System.out.println(result); pool.shutdown(); }}