JUC 源码解读系列--FutureTask 篇

前面在介绍线程池 ThreadPoolExecutor 时,只关注了执行任务的方法 execute(),ThreadPoolExecutor 中执行任务还有另一个方法–submit(),此方法支持带返回值的线程任务。其底层是基于 FutureTask 来实现的,该类也是 JUC 中提供的并发工具,本篇我们一起走进它的实现细节。

版本说明:本次源码解读基于 JDK1.8,请注意区分。

1. 基本用法

        Callable<String> callable = new Callable() {
            @Override
            public String call() throws Exception {
            	String result;
                // do computing
                return result;
            }
        };

        ThreadPoolExecutor executor = new ThreadPoolExecutor(3,
                5,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>());
        try {
            Future<String> futureTask = executor.submit(callable);

            try {
                System.out.println(futureTask.get());
            } catch (ExecutionException e) {
            }
        }
        finally {
            executor.shutdown();
        }

我们看一下 ThreadPoolExecutor 中 submit() 方法的实现:

// 此方法继承自 ThreadPoolExecutor 的父类 AbstractExecutorService
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

2. 类关系图

在这里插入图片描述
从上图可以看出,FutureTask 实现了 RunnableFuture 接口,而 RunnableFuture 接口分别继承了 Runnable 和 Future 接口。Runnable 接口是我们所熟悉的创建线程的一种方式,因此我们重点看一下 Future 接口:

2.1 Future

// Future 代表一个异步计算的结果。
// 它提供了一些方法:检查任务的完成状态,等待并获取任务结果,以及取消任务。
// 如果我们使用 Future 只是为了使用取消任务的功能,可以将 类型声明为 Future<?>,
// 并且返回 null 作为任务的执行结果即可。
public interface Future<V> {

    // 尝试取消任务的执行。
    // 如果任务已经完成或者已经被取消,将会尝试失败。
    // 如果执行此方法时任务还没有开始执行,这个任务将不会被执行。
    // 如果执行此方法时任务正在执行,
    // mayInterruptIfRunning 参数来控制是否进行中断。
    // 此方法返回后,之后对 isDone() 方法的调用总是返回 true。
    // 此方法返回后,如果返回 true,之后对 isCancelled() 方法的调用总是返回 true。
    boolean cancel(boolean mayInterruptIfRunning);

    // 任务是否已经被成功取消。
    // 任务结束前被成功取消,返回真。
    boolean isCancelled();

    // 任务是否已经完成。
    // 任务正常结束,异常结束,或者被成功取消,都算作完成。
    boolean isDone();

    // 等待计算的完成,并获取执行的结果。
    V get() throws InterruptedException, ExecutionException;

	// 等待计算的完成,并获取执行的结果。带超时时间。
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

3. FutureTask 实现原理

3.1 属性及构造方法

    /**
     * The run state of this task, initially NEW.  The run state
     * transitions to a terminal state only in methods set,
     * setException, and cancel.  During completion, state may take on
     * transient values of COMPLETING (while outcome is being set) or
     * INTERRUPTING (only while interrupting the runner to satisfy a
     * cancel(true)). Transitions from these intermediate to final
     * states use cheaper ordered/lazy writes because values are unique
     * and cannot be further modified.
     *
     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    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;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    // 任务的计算结果,对 outcome 的修改都是基于 state 的 CAS 操作,
    // 因此不存在线程安全问题。
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    // 初始化 state 为 NEW
    this.state = NEW;       // ensure visibility of callable
}

public FutureTask(Runnable runnable, V result) {
	// 通过工具类 Executors 获得一个适配器
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

Executors.callable()

public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}

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;
    }
}

3.2 get()

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    // 如果任务还未完成,等待。
    // 任务的完成包含正常结束、异常结束以及取消
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}

awaitDone()

private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    final long deadline = 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();
        // waiters 是栈的结构,将新的节点压栈
        else if (!queued)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        // 超时判断
        else if (timed) {
        	// 如果等待的线程已经超时,移除对应节点,返回当前任务状态
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
            	// 超时,移除节点
                removeWaiter(q);
                return state;
            }
            // 带超时时间的挂起
            LockSupport.parkNanos(this, nanos);
        }
        else
            LockSupport.park(this);
    }
}

report()

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);
}

3.3 cancel()

public boolean cancel(boolean mayInterruptIfRunning) {
	// 只能取消还未执行的任务。
	// 如果允许中断,先更新成中间状态,中断线程之后再更新成已中断
    if (!(state == NEW &&
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
    	// 唤醒所有等待获取任务执行结果的线程
        finishCompletion();
    }
    return true;
}

finishCompletion()

private void finishCompletion() {
    // assert state > COMPLETING;
    for (WaitNode q; (q = waiters) != null;) {
        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;
                q.next = null; // unlink to help gc
                q = next;
            }
            break;
        }
    }

	// 供用户扩展的方法
    done();

    callable = null;        // to reduce footprint
}

3.4 run()

public void run() {
	// 任务开始执行时,任务的状态必须是 NEW。
	// 将任务的 runner 设置成当前线程,设置失败说明任务已被其他线程执行。
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        // 对 state 进行双重检查
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
            	// 任务执行正常结束,得到执行结果
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
            	// 任务异常结束,将异常作为运行结果
                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()
        // 退出任务执行前,将任务的 runner 置空
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        // 取消任务的方法中,先获取执行任务的线程,再对线程进行非空判断并中断线程。
        // 假如非空判断成立后,上面的 runner = null; 才被执行,可能导致泄露。
        // 因此这里要对状态重新判断并进行相应处理。
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

set()

// 将 callable 的运行结果作为计算结果返回
protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        // 唤醒等待计算完成的线程
        finishCompletion();
    }
}

setException()

// 将异常信息设置成任务的计算结果
protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = t;
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        // 唤醒等待任务结束的线程
        finishCompletion();
    }
}

handlePossibleCancellationInterrupt()

private void handlePossibleCancellationInterrupt(int s) {
    // It is possible for our interrupter to stall before getting a
    // chance to interrupt us.  Let's spin-wait patiently.
    // 任务的状态被置为 INTERRUPTING 之后,才去调用线程的中断操作,
    // 如果此时线程还未被中断,让出时间片等待中断操作。
    if (s == INTERRUPTING)
        while (state == INTERRUPTING)
            Thread.yield(); // wait out pending interrupt

    // assert state == INTERRUPTED;

    // We want to clear any interrupt we may have received from
    // cancel(true).  However, it is permissible to use interrupts
    // as an independent mechanism for a task to communicate with
    // its caller, and there is no way to clear only the
    // cancellation interrupt.
    //
    // Thread.interrupted();
}

4. 总结

FutureTask 实现了 RunnableFuture 接口,对 Runnable 和 Future 接口进行了实现。run() 方法中实现了任务的执行及计算结果的设置。对 Future 接口的实现,提供了任务执行状态的检查,任务的取消操作,以及任务结果的获取。

在任务执行完毕(包含正常结束,异常结束,被取消)之前,等待获取执行结果的线程一直处于阻塞状态(超时除外),这些线程被构造成 WaitNode 节点对象压入栈中。当任务执行完毕后,会唤醒所有的等待结果的线程并返回执行的结果,如果执行过程中出现异常,则抛出 ExecutionException 异常,如果任务是被取消而结束的,则抛出 CancellationException 异常。

FutureTask 中使用状态机制来管理任务的生命周期。
各个状态的含义如下:

  • NEW:初始状态,任务还未被取消或被线程执行。
  • COMPLETING:任务执行正常或异常结束,outcome 被设置之前的状态。
  • NORMAL:任务执行正常结束,outcome 被设置之后的状态。
  • EXCEPTIONAL:任务执行异常结束,outcome 被设置之后的状态。
  • CANCELLED:任务被 cancel(false) 方法调用取消后的状态。
  • INTERRUPTING:任务被 cancel(true) 方法调用取消时,线程被中断前的中间状态。
  • INTERRUPTED:任务被 cancel(true) 方法调用取消后的状态。

状态的转换如下:

  • 任务正常结束:NEW -> COMPLETING -> NORMAL
  • 任务异常结束:NEW -> COMPLETING -> EXCEPTIONAL
  • 任务被 cancel(false) 方法调用取消:NEW -> CANCELLED
  • 任务被 cancel(true) 方法调用取消:NEW -> INTERRUPTING -> INTERRUPTED
<think>嗯,用户之前让我介绍过Java多线程的基础知识,现在他们想深入了解JUCjava.util.concurrent)的源码。这说明用户可能已经掌握了多线程的基本概念和使用方法,现在想更深入理解底层实现,提升自己的技术水平。 首先,我需要确定用户对JUC的了解程度。他们可能已经用过一些工具类,比如ReentrantLock或CountDownLatch,但现在想了解这些类内部的机制。用户可能是中高级开发者,希望优化代码性能,或者准备面试,需要深入理解并发框架。 接下来,我需要考虑如何组织内容。直接深入源码可能会让用户感到困惑,所以应该先从JUC的整体结构入手,然后分模块解析核心类。比如,原子类、锁、同步器、并发集合、线程池等,每个部分都需要挑选关键点进行讲解。 用户可能需要知道关键设计模式,比如AQS(AbstractQueuedSynchronizer),因为这是JUC的核心。需要详细解释AQS的工作原理,如CLH队列、状态管理等。此外,CAS操作和volatile关键字也是实现无锁并发的基础,需要结合源码说明它们的作用。 另外,用户可能对常见类的实现感兴趣,比如ReentrantLock的公平锁与非公平锁区别,ConcurrentHashMap的分段锁或CAS优化,线程池的任务调度流程等。需要结合代码示例和流程图,帮助用户理解这些复杂机制。 同时,要注意避免过于冗长的代码展示,而是聚焦在关键方法和设计思路上。例如,AQS的acquire和release方法,以及它们如何被ReentrantLock或Semaphore调用。此外,可以比较不同版本的实现变化,比如JDK1.7和1.8中ConcurrentHashMap的改进,让用户了解优化方向。 最后,需要给出学习建议,推荐用户如何逐步深入源码,比如从简单的AtomicInteger开始,再逐步研究AQS和锁的实现。同时,提醒用户注意调试和测试的重要性,通过实际案例加深理解。 总结下来,回答的结构应该是先概述JUC的重要性,再分模块解析核心源码,结合关键设计模式和具体类实现,最后给出学习方法和资源推荐。这样既系统又深入,帮助用户由浅入深地掌握JUC源码。</think>好的!`java.util.concurrent`(JUC)是 Java 并发编程的核心工具包,包含大量高性能、线程安全的工具类和框架。深入源码理解其设计思想,能帮助你掌握高并发编程的精髓。以下从 **核心模块**、**设计模式**、**关键类源码分析** 三个维度进行详细解读--- ### **一、JUC 整体架构** JUC 的核心模块包括: 1. **原子类**(Atomic):基于 CAS 的无锁并发操作(如 `AtomicInteger`)。 2. **锁与同步器**(Locks):如 `ReentrantLock`、`AQS`(AbstractQueuedSynchronizer)。 3. **并发容器**(Collections):如 `ConcurrentHashMap`、`CopyOnWriteArrayList`。 4. **线程池**(Executors):`ThreadPoolExecutor`、`ForkJoinPool`。 5. **同步工具**(Tools):`CountDownLatch`、`CyclicBarrier`、`Semaphore`。 6. **并发流与 Future**:如 `CompletableFuture`、`ForkJoinTask`。 --- ### **二、核心源码解读** #### **1. AQS(AbstractQueuedSynchronizer)** AQS 是 JUC 锁和同步器的基石(如 `ReentrantLock`、`Semaphore` 均基于它实现)。 ##### **核心设计** - **状态变量(state)**:通过 `volatile int state` 表示资源状态(如锁的持有次数)。 - **CLH 队列**:一个双向链表实现的等待队列,存储竞争资源的线程。 - **模板方法模式**:子类只需实现 `tryAcquire`/`tryRelease`,AQS 负责线程排队和唤醒。 ##### **源码关键方法** ```java // 独占模式获取资源(如 ReentrantLock.lock()) public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } // 子类需实现 tryAcquire(如判断 state 是否为0) protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); } ``` ##### **实现示例:ReentrantLock 的公平锁** ```java // 公平锁的 tryAcquire 实现 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && // 关键:检查队列是否有等待线程 compareAndSetState(0, acquires)) { // CAS 更新状态 setExclusiveOwnerThread(current); return true; } } // 处理重入逻辑(state 增加) else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; setState(nextc); return true; } return false; } ``` --- #### **2. 原子类(AtomicInteger)** 原子类的核心是 **CAS(Compare-And-Swap)** 操作,直接依赖 CPU 指令(如 x86 的 `cmpxchg`)。 ##### **源码关键实现** ```java public class AtomicInteger extends Number implements java.io.Serializable { private volatile int value; // 通过 volatile 保证可见性 public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } } // Unsafe 类的 CAS 操作(native 方法) public final class Unsafe { public final native boolean compareAndSwapInt( Object o, long offset, int expected, int x); } ``` ##### **CAS 的 ABA 问题** - **问题**:变量从 A 改为 B 又改回 A,CAS 无法感知中间变化。 - **解决方案**:使用 `AtomicStampedReference` 添加版本号。 --- #### **3. ConcurrentHashMap(JDK 1.8)** JDK 1.8 摒弃分段锁,改用 **CAS + synchronized** 实现更细粒度的锁。 ##### **关键设计** - **Node 数组(table)**:每个桶位(bucket)是一个链表或红黑树。 - **并发控制**: - **CAS 初始化数组**:避免多线程重复初始化。 - **synchronized 锁桶头节点**:仅锁定单个桶,减少竞争。 ##### **putVal 方法核心逻辑** ```java final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException(); int hash = spread(key.hashCode()); int binCount = 0; for (Node<K,V>[] tab = table;;) { Node<K,V> f; int n, i, fh; if (tab == null || (n = tab.length) == 0) tab = initTable(); // CAS 初始化数组 else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value))) break; // CAS 插入新节点 } else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); // 协助扩容 else { synchronized (f) { // 锁住桶头节点 // 插入链表或红黑树 } } } } ``` --- #### **4. ThreadPoolExecutor** 线程池的核心是 **任务队列** 和 **线程调度策略**。 ##### **核心参数** - **corePoolSize**:核心线程数(即使空闲也不销毁)。 - **workQueue**:任务队列(如 `LinkedBlockingQueue`)。 - **RejectedExecutionHandler**:拒绝策略(如丢弃、抛异常)。 ##### **execute 方法逻辑** ```java public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) // 创建新线程执行任务 return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { // 任务入队 // 二次检查线程池状态 } else if (!addWorker(command, false)) // 尝试创建非核心线程 reject(command); // 触发拒绝策略 } ``` --- ### **三、JUC 设计模式与技巧** 1. **CAS 无锁编程** - 通过 `Unsafe` 类直接操作内存,避免锁竞争(如原子类、ConcurrentHashMap)。 - 适用场景:读多写少,竞争不激烈。 2. **模板方法模式** - AQS 将公共逻辑(如线程排队)抽象到基类,子类只需实现特定方法(如 `tryAcquire`)。 3. **写时复制(Copy-On-Write)** - `CopyOnWriteArrayList` 在修改时复制整个数组,保证读操作无锁。 4. **分段锁优化** - JDK 1.7 的 `ConcurrentHashMap` 使用分段锁(Segment),减少锁粒度。 --- ### **四、高频源码面试题** 1. **AQS 的 CLH 队列为什么是双向链表?** - 答:为了支持取消操作(如超时或中断),需要快速定位前驱节点。 2. **ConcurrentHashMap 1.7 和 1.8 的区别?** - 1.7:分段锁 + ReentrantLock; - 1.8:CAS + synchronized + 红黑树。 3. **线程池如何回收空闲线程?** - 答:`ThreadPoolExecutor` 通过 `getTask()` 中的 `poll(keepAliveTime)` 实现超时回收。 --- ### **五、学习建议** 1. **调试源码**:通过 IDE 调试 `ReentrantLock.lock()` 的执行流程。 2. **阅读注释**:JUC 类的源码注释通常包含设计思路和并发规则。 3. **手写简化版**:尝试实现一个简单的 AQS 或线程池。 理解 JUC 源码不仅能提升并发编程能力,还能深入掌握 Java 的高性能设计哲学。建议从 `AtomicInteger` 和 `ReentrantLock` 入手,逐步扩展到复杂类如 `ConcurrentHashMap` 和 `ThreadPoolExecutor`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值