【并发编程】从入门到源码分析有返回值的线程任务:FutureTask

Callable、Future、FutureTask基本介绍

  • 直接继承Thread或者实现Runnable接口都可以创建线程,但是这两种方法都有一个问题就是:没有返回值,也就是不能获取执行完的结果。
  • 因此jdk1.5就提供了Callable接口来实现这一场景,而Future和FutureTask就可以和Callable接口配合起来使用。

Callable和Runnable的区别

  • Runnable不能返回一个返回值,不能抛出 checked Exception。
  • Callable的call方法可以有返回值,可以声明抛出异常。
  • Callable 配合的有一个 Future 类,通过 Future 可以了解任务执行情况,或者取消任务的执行,还可获取任务执行的结果,这些功能都是 Runnable 做不到的,Callable 的功能要比 Runnable 强大。

FutureTask的父类Future介绍

  • Future最常用的实现是:FutureTask(由于他继承的RunnableFuture实现了Runnable和Future)
  • boolean cancel (boolean mayInterruptIfRunning):取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束。
  • boolean isCancelled ():任务是否已经取消,任务正常完成前将其取消,则返回 true。
  • boolean isDone ():任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true。
  • V get () throws InterruptedException, ExecutionException:等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException。
  • V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException:同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException

Future的注意事项

  • 当 for 循环批量获取 Future 的结果时容易长时间阻塞,get 方法调用时应使用 timeout 限制。
  • Future 的生命周期不能后退。一旦完成了任务,它就永久停在了“已完成”的状态,不能从头再来。

Future的缺陷

  • 并发执行多任务:Future只提供了get()方法来获取结果,并且是阻塞的。所以,除了等待你别无他法;
  • 无法对多个任务进行链式调用:如果你希望在计算任务完成后执行特定动作,比如发邮件,但Future却没有提供这样的能力;
  • 无法组合多个任务:如果你运行了10个任务,并期望在它们全部执行结束后执行特定动作,那么在Future中这是无能为力的;
  • 没有异常处理:Future接口中没有关于异常处理的方法;

使用方式:把串行的任务并行化

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class FutureTest {

	public static void main(String[] args) throws ExecutionException, InterruptedException {

		FutureTask<String> ft1 = new FutureTask<>(new T1Task());
		FutureTask<String> ft2 = new FutureTask<>(new T2Task());
		FutureTask<String> ft3 = new FutureTask<>(new T3Task());
		FutureTask<String> ft4 = new FutureTask<>(new T4Task());
		FutureTask<String> ft5 = new FutureTask<>(new T5Task());

		// 构建线程池
		ExecutorService executorService = Executors.newFixedThreadPool(5);
		executorService.submit(ft1);
		executorService.submit(ft2);
		executorService.submit(ft3);
		executorService.submit(ft4);
		executorService.submit(ft5);
		// 获取执行结果
		System.out.println(ft1.get());
		System.out.println(ft2.get());
		System.out.println(ft3.get());
		System.out.println(ft4.get());
		System.out.println(ft5.get());

		executorService.shutdown();

	}

	static class T1Task implements Callable<String> {
		@Override
		public String call() throws Exception {
			System.out.println("T1:任务正在执行...");
			TimeUnit.MILLISECONDS.sleep(50);
			return "T1:执行完成!";
		}
	}

	static class T2Task implements Callable<String> {
		@Override
		public String call() throws Exception {
			System.out.println("T2:任务正在执行...");
			TimeUnit.MILLISECONDS.sleep(50);
			return "T2:执行完成!";
		}
	}

	static class T3Task implements Callable<String> {
		@Override
		public String call() throws Exception {
			System.out.println("T3:任务正在执行...");
			TimeUnit.MILLISECONDS.sleep(50);
			return "T3:执行完成!";
		}
	}

	static class T4Task implements Callable<String> {
		@Override
		public String call() throws Exception {
			System.out.println("T4:任务正在执行...");
			TimeUnit.MILLISECONDS.sleep(50);
			return "T4:执行完成!";
		}
	}

	static class T5Task implements Callable<String> {
		@Override
		public String call() throws Exception {
			System.out.println("T5:任务正在执行...");
			TimeUnit.MILLISECONDS.sleep(50);
			return "T5:执行完成!";
		}
	}
}
  • 运行结果
    运行结果.png

FutureTask的状态源码分析

// 记录实际的状态:volatile修饰的,FutureTask的核心!
private volatile int state;
// 新的任务,初始状态。
private static final int NEW          = 0;
// 正在执行:任务被设置结果时,这是一个中间状态。
private static final int COMPLETING   = 1;
// 任务正常结束。
private static final int NORMAL       = 2;
// 任务因异常而结束。
private static final int EXCEPTIONAL  = 3;
// 任务还未执行之前就调用了cancel(true)方法
private static final int CANCELLED    = 4;
// 任务调用了cancel(true),但是还没有中断。
private static final int INTERRUPTING = 5;
// 任务中断了!
private static final int INTERRUPTED  = 6;

FutureTask的构造方法源码分析

/**
 * callable:一个实现了call方法的任务
 */
public FutureTask(Callable<V> callable) {
    // 没有任务,直接抛出异常
    if (callable == null)
        throw new NullPointerException();
    // 给任务赋值
    this.callable = callable;
    // 设置线程状态是新创建状态
    this.state = NEW;       // ensure visibility of callable
}

/**
 * callable:一个实现了call方法的任务
 * result:FutureTask在Runnable执行完之后返回预先设置好的result,以便通知任务已完成.
 */
public FutureTask(Runnable runnable, V result) {
    // 得到callable对象
    this.callable = Executors.callable(runnable, result);
    // 设置线程状态是新创建状态
    this.state = NEW;       // ensure visibility of callable
}

/**
 * 得到Callable对象
 */
public static <T> Callable<T> callable(Runnable task, T result) {
    // 任务为空,直接抛异常
    if (task == null)
        throw new NullPointerException();
    // 返回具体适配器的任务
    return new RunnableAdapter<T>(task, result);
}

/**
 * 具体适配器的获取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;
    }
    // 这里是定义的call方法
    public T call() {
        task.run();
        return result;
    }
}

FutureTask的run方法源码分析

/**
 * FutureTask任务实际线程执行的run方法
 */
public void run() {
    // 状态不是新建状态:可能被其他线程执行了。
    // 当前线程状态为null,并且可以cas变为当前的线程:失败的话说明被其他线程执行了。
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        // 得到具体的callable对象
        Callable<V> c = callable;
        // 对象不是空的,并且对象是新建状态
        if (c != null && state == NEW) {
            // 定义结果
            V result;
            // 定时执行方式的标识
            boolean ran;
            try {
                // 这里调用的是我们具体的自定义子类
                result = c.call();
                // 标记正常执行完成
                ran = true;
            } catch (Throwable ex) {
                // 出现异常结果为null
                result = null;
                // 标记为异常执行
                ran = false;
                // 出现异常后的设置异常方法
                setException(ex);
            }
            // 如果是正常执行,设置执行后的结果
            if (ran)
                // 设置正常执行后的结果
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        // 将当前任务的线程变为null:防止对run()的并发调用
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        // 得到当前任务的状态
        int s = state;
        // 当前的任务正在中断或者已经中断
        if (s >= INTERRUPTING)
            // 处理可能的取消中断
            handlePossibleCancellationInterrupt(s);
    }
}

/**
 * 出现异常后的设置异常方法
 */
protected void setException(Throwable t) {
    // 可以CAS状态到设置结果状态
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        // 标记get方法抛出异常
        outcome = t;
        // CAS的方式设置这个任务由于异常结束
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        // 最终任务执行完成的方法
        finishCompletion();
    }
}

/**
 * 最终任务执行完成的方法:线程执行完后,会在finishCompletion()方法中进行唤醒WaitNode节点中等待的线程。
 */
private void finishCompletion() {
    // assert state > COMPLETING;
    // 遍历WaitNode中的每一个节点,一直到没有需要等待唤醒的线程。
    for (WaitNode q; (q = waiters) != null;) {
        // 当前线程通过CAS能把持有的线程清除掉
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            for (;;) {
                // 得到当前节点的线程
                Thread t = q.thread;
                // 如果当前节点存在线程,唤醒这个线程
                if (t != null) {
                    // 当前节点
                    q.thread = null;
                    // 唤醒操作
                    LockSupport.unpark(t);
                }
                // 得到等待队列下一个节点
                WaitNode next = q.next;
                // 下一个节点为空,跳出内部循环
                if (next == null)
                    break;
                // 当前节点的下一个节点指向变为空,方便GC去回收
                q.next = null; // unlink to help gc
                // 链表去除第一个元素
                q = next;
            }
            // 跳出外部循环
            break;
        }
    }
    // 模板方法,可以被覆盖重写。
    done();

    // 情况具体的任务
    callable = null;        // to reduce footprint
}

/**
 * 设置执行后的结果
 */
protected void set(V v) {
    // 可以CAS状态到设置结果状态
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        // 设置执行后的结果
        outcome = v;
        // CAS的方式设置这个任务正常结束
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        // 最终任务执行完成的方法
        finishCompletion();
    }
}

/**
 * 处理可能的取消中断
 */
private void handlePossibleCancellationInterrupt(int s) {
    // 当前的状态是任务调用了cancel(true),但是还没有中断。
    if (s == INTERRUPTING)
        // 等待他成功中断!
        while (state == INTERRUPTING)
            Thread.yield(); // wait out pending interrupt
}

FutureTask的get方法源码分析

/**
 * FutureTask任务实际用来获取的方法
 */
public V get() throws InterruptedException, ExecutionException {
    // 得到当前的任务状态
    int s = state;
    // 任务新创建或者正在被执行
    if (s <= COMPLETING)
        // 等待任务执行完毕,如果任务取消或者超时则停止
        s = awaitDone(false, 0L);
    // 返回具体的结果
    return report(s);
}

/**
 * FutureTask任务实际用来获取的方法,有超时时间
 */
public V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException {
    // 时间单位是空的,抛异常
    if (unit == null)
        throw new NullPointerException();
    // 得到当前的任务状态
    int s = state;
    // 当前任务在执行或者新创建的时候,获取超时了,抛出超时异常
    if (s <= COMPLETING &&
        (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
        throw new TimeoutException();
    // 返回具体的结果
    return report(s);
}

/**
 * 等待任务执行完毕,如果任务取消或者超时则停止
 */
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    // 计算最后的时间期限
    final long = timed ? System.nanoTime() + nanos : 0L;
    // 得到当前的节点
    WaitNode q = null;
    // 得到是否排队的标识
    boolean queued = false;
    for (;;) {
        // 如果线程被中断
        if (Thread.interrupted()) {
            // 在等待队列中删除当前的节点
            removeWaiter(q);
            // 抛出中断异常
            throw new InterruptedException();
        }

        // 得到当前任务的状态
        int s = state;
        // 任务已经执行完成(正常执行完成或者被)
        if (s > COMPLETING) {
            // 当前节点不为空的时候,移除当前节点执行的线程
            if (q != null)
                q.thread = null;
            // 将结果返回
            return s;
        }
        // 当前节点正在执行,等待他执行完成
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        // 当前的节点还没有
        else if (q == null)
            // 定义新节点
            q = new WaitNode();
        // 当前节点有排队
        else if (!queued)
            // CAS的方式这种下一个节点
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        // 存在超时时间
        else if (timed) {
            // 计算剩余时间
            nanos = deadline - System.nanoTime();
            // 剩余时间小于0,说明超时了
            if (nanos <= 0L) {
                // 在等待队列中删除当前的节点
                removeWaiter(q);
                // 返回运行的状态
                return state;
            }
            // 阻塞:超过时间自动唤醒
            LockSupport.parkNanos(this, nanos);
        }
        else
            // 阻塞
            LockSupport.park(this);
    }
}

/**
 * 返回具体的结果
 */
private V report(int s) throws ExecutionException {
    // 得到具体的结果
    Object x = outcome;
    // 任务正常结束,返回正常结果
    if (s == NORMAL)
        return (V)x;
    // 任务呗取消或者中断,返回取消异常
    if (s >= CANCELLED)
        throw new CancellationException();
    // 剩余的结果只能是由于异常原因结束的,返回具体的异常
    throw new ExecutionException((Throwable)x);
}

结束语

  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
  • 关注公众号,后续持续高效的学习JVM!
  • 这个公众号,无广告!!!每日更新!!!
    作者公众号.jpg
posted @ 2022-02-20 23:37  程序java圈  阅读(45)  评论(0编辑  收藏  举报