FutureTask的理解

我们都知道实现线程的方式有三种,分别是继承Thread类、实现Runnable接口和实现Callable接口。大致说一下使用场景:

  • 继承Thread类:如果类已经继承了其他接口,就不能通过此种方式实现线程了
  • 实现Runnable接口:不需要返回结果的线程
  • 实现Callable接口:需要返回结果

线程返回值是通过FutureTask拿到的结果,下面是FutureTask类的继承关系:

下面通过一个例子说明一下怎么获取线程的执行结果:

//定义一个类,实现Callable接口
public
class MyCallable implements Callable { @Override public Object call() throws Exception {
     //睡眠5秒中,模拟任务的执行 TimeUnit.SECONDS.sleep(
5);
     //返回的结果值
return "5s OK"; } }
public class Main {

    public static void main(String[] args) throws Exception{

        System.out.println("----------main start----------");

        //通过构造方法传入实现Callable接口的实例
        //构造方法也可以传入实现Runnable接口的实例,最终还是适配器转换成了Callable
        FutureTask<String> futureTask = new FutureTask<String>(new MyCallable());

        //FutureTask最终还是一个实现了Runnable接口的类,最终还是需要开启一个线程去执行任务
        new Thread(futureTask).start();

        //get是阻塞方法,取到值之前会一直阻塞
        String value = futureTask.get();
        System.out.println("thread result:" + value);

        System.out.println("----------main end----------");
    }
}

最终的执行结果:

源码分析

 //Thread中的start方法
public synchronized void start() { //判断线程状态,不是新建状态就抛出异常 if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; //调用本地方法,开启一个线程 start0(); started = true; } //本地方法 private native void start0();

从源码可以看出调用Thread.start()方法后会调用本地的start0()方法开启线程,具体执行逻辑是在run()方法中。

//Thread中的run方法调用了实现Runnable接口的run()方法
@Override
public void run() { if (target != null) { target.run(); } }

从类图中可以看到FutureTask是实现了Runnable接口的类,所以Thread中的run()方法最终执行的是FutureTask中的run()方法,下面是源码:

public void run() {
     //如果不是新建状态,直接返回
if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try {
            //调用Callable中的call方法 result
= c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { runner = null; int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }

其实FutureTask还是比较简单,Callable就是他的任务,而FutureTask内部维护了一个任务状态,所有的状态都是围绕这个任务来进行的,随着任务的进行,状态也在不断的更新。任务发起者调用get()方法时,如果任务没有执行完成,会将当前线程放入阻塞队列等待,当任务执行完后,会唤醒阻塞队列中的线程。

 

posted @ 2020-08-01 17:10  温故方能知新  阅读(375)  评论(0编辑  收藏  举报