FutureTask源码分析

一、简介

        最近在分析Executor框架源码体系,先从FutureTask类开始介绍,在Executor框架体系中,FutureTask用来表示可获取结果的异步任务。FutureTask实现了Future接口和Runnable,FutureTask提供了查询异步任务是否计算结束以及获取最终的异步任务的结果以及取消异步任务的一些常用的方法,源码对应的jdk版本是1.8。

二、类关系

                            

  1.  Future类

    public interface Future<V> {
        //提供取消任务的抽象方法
        boolean cancel(boolean mayInterruptIfRunning);
    
        //提供判断任务是否取消的抽象方法
        boolean isCancelled();
        
        //提供判断任务是否完成的抽象方法
        boolean isDone();
        
        //提供获取异步任务的执行结果的抽象方法,会抛出中断异常和执行时异常
        V get() throws InterruptedException, ExecutionException;
        
        //提供超时的获取异步任务的执行结果的抽象方法,会抛出中断异常、超时异常、执行时异常
        V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    }     复制代码
  2.  Runnable类

    //函数式接口,不清楚的看下我的另一篇函数式接口介绍https://juejin.im/post/5c7d1254e51d45720f72264c
    @FunctionalInterface
    public interface Runnable {
        //只有一个run方法
        public abstract void run();
    }复制代码

  3. RunnableFuture类

    //实现于Runnable、Future接口
    public interface RunnableFuture<V> extends Runnable, Future<V> {
        //抽象run方法
        void run();
    }复制代码

        

三、成员变量

//state是FutureTask的状态,任务的状态,state状态流转存在4种情况
//任务顺利执行:NEW -> COMPLETING -> NORMAL
//任务执行异常:NEW -> COMPLETING -> EXCEPTIONAL
//任务取消:NEW -> CANCELLED
//任务中断:NEW -> INTERRUPTING -> INTERRUPTED
private volatile int state;
//任务初始化状态
private static final int NEW          = 0;
//任务已经完成,但结果还没有被赋值给outcome,COMPLETING在下面的介绍set方法和setException方法中会看到,属于中间态
private static final int COMPLETING   = 1;
//任务正确执行完成的状态,属于终态
private static final int NORMAL       = 2;
//任务执行出现异常的状态,属于终态
private static final int EXCEPTIONAL  = 3;
//任务在NEW状态调用cancel方法的取消状态,下面cancel方法会介绍到,属于终态
private static final int CANCELLED    = 4;
//任务被中断中的状态,即线程Thread还没调用interrupt方法前的状态,属于中间态
private static final int INTERRUPTING = 5;
//即线程Thread调用interrupt方法后的状态,属于终态
private static final int INTERRUPTED  = 6;
//任务的执行体
private Callable<V> callable;
//最终输出结果,任务正确执行完的最终结果,或者任务执行出现异常的异常对象
private Object outcome;
//执行当前任务的线程
private volatile Thread runner;
//等待队列,当前任务在其他线程中还没执行完,其他线程调用当前FutureTask对象的get方法,会将其线程阻塞起来,包装成队列,比如有个FutureTask在线程t中执行,还未执行完(COMPLETING状态之前才会阻塞,在下面的awaitDone方法会详细介绍),主线程调用此任务的get方法
private volatile WaitNode waiters;
//不清楚UnSafe类如何使用的,可以看下我分享的美团UnSafe的使用
private static final sun.misc.Unsafe UNSAFE;
//state属性的相对偏移量,为什么定义成静态的,是因为属性的相对所有实例的偏移量都是相等的
private static final long stateOffset;
//runner属性的相对偏移量
private static final long runnerOffset;
//waiters属性的相对编译量
private static final long waitersOffset;
static {
    try {
        //如果想在自己的项目中使用UnSafe类,也可以看下我的UnSafe分享
        UNSAFE = sun.misc.Unsafe.getUnsafe();
        //获取FutureTask的Class对象,因为Class文件中的内存布局,属性相对所有的实例的相对偏移量都是相等的,想深究的同学可以看下jvm的相关知识
        Class<?> k = FutureTask.class;
        //先获取属性,再获取属性相对实例的偏移量
        stateOffset = UNSAFE.objectFieldOffset
            (k.getDeclaredField("state"));
        runnerOffset = UNSAFE.objectFieldOffset
            (k.getDeclaredField("runner"));
        waitersOffset = UNSAFE.objectFieldOffset
            (k.getDeclaredField("waiters"));
    } catch (Exception e) {
        throw new Error(e);
    }
}复制代码

四、内部类

  1. 静态内部类WaitNode

    static final class WaitNode {
            //被阻塞的线程,当前线程
            volatile Thread thread;
            //此节点的下一个节点
            volatile WaitNode next;
            //构造函数,将当前线程赋值于thread属性,就向上面所介绍的,如t线程在执行任务,主线程main调用任务的get方法,阻塞当前线程,即朱线程
            WaitNode() { thread = Thread.currentThread(); }
    }复制代码

五、构造函数

//传入callable对象的构造函数,Callable和Runnable的主要区别是,Callable可以拿到执行结果
public FutureTask(Callable<V> callable) {
        //如果callable对象为空,抛出空指针 
        if (callable == null)
            throw new NullPointerException();
        //将其传入进来的callable对象赋值给FutureTask实例的callable属性
        this.callable = callable;
        //将FutureTask的state属性赋值为初始化状态,状态和状态的转换可以看上面属性的介绍
        this.state = NEW;
}

//传入runnable对象,和返回结果对象,此返回结果对象要在runnable对象中被使用,即将runnable对象的执行结果赋值给result对象。此构造函数也是将Runnable对象转换为Callable对象再进行操作
public FutureTask(Runnable runnable, V result) {
    //Executors.callable方法,可以看下面对该方法的介绍,在此只会先对Executors的callable方法进行介绍,不会详细介绍Executors类的所有方法,等下一篇再详细对Executors进行详细介绍
    this.callable = Executors.callable(runnable, result);
    //将FutureTask的state属性赋值为初始化状态,状态和状态的转换可以看上面属性的介绍
    this.state = NEW;
}

//Executors.callable方法
public static <T> Callable<T> callable(Runnable task, T result) {
    //如果runnable任务为空,抛出空指针异常
    if (task == null)
        throw new NullPointerException();
    //RunnableAdapter类是Executors的静态内部类,看下面对此类的介绍 
    return new RunnableAdapter<T>(task, result);
}

//Executors的静态内部类,使用适配模式,将Runnable实例对象做为Callable的子类RunnableAdapter的task属性,即组合优于继承
static final class RunnableAdapter<T> implements Callable<T> {
    //将Runnable的实例对象,做为task属性
    final Runnable task;
    //传入进来的返回对象实例
    final T result;
    //构造函数,将其Runnable实例对象和返回结果对象做为RunnableAdapter的两个属性
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    //call方法
    public T call() {
        //调用runnable的run方法
        task.run();
        //返回传入进来的结果对象,此result对象需要run方法中使用,不然没法拿到runnable实例的执行结果
        return result;
    }
}
复制代码

六、方法

//FutureTask的执行方法
public void run() {
        //如果状态不是初始化状态,直接返回,避免重复执行,或者使用UnSafe的cas将FutureTask实例的runner属性设置为当前线程,即将要执行该任务的线程,如果设置失败也直接返回,表明此任务有线程正在执行,避免重复执行
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            //当前callable任务不为空,并且当前FutureTask状态为初始化状态
            if (c != null && state == NEW) {
                //callable执行后的返回结果
                V result;
                //任务执行是否成功
                boolean ran;
                try {
                    //调用callable的call方法,获取返回结果
                    result = c.call();
                    //如果callable方法的call方法正常执行,将ran标志位设置为true
                    ran = true;
                } catch (Throwable ex) {
                    //如果线程在执行callable的call方法出现异常,将callable的返回结果的存储对象置为空
                    result = null;
                    //将标志位设置为false,表示任务执行失败
                    ran = false;
                    //FutureTask实例状态的推进,以及将异常的对象赋值给FutureTask的outcome属性,看下面setException方法的介绍
                    setException(ex);
                }
                //如果任务执行成功
                if (ran)
                    //FutureTask状态的推进,以及callable对象实例的call方法的返回结果赋值给FutureTask的outcome属性,看下面set方法的详细介绍
                    set(result);
            }
        } finally {
            //最后将记录执行当前任务的线程的FutureTask的属性runner置为空
            runner = null;
            //获取当前FutureTask实例的属性state状态值 
            int s = state;
            //如果当前状态属于中断中,或者已中断,执行handlePossibleCancellationInterrupt方法
            if (s >= INTERRUPTING)
                //看下面handlePossibleCancellationInterrupt方法的介绍
                handlePossibleCancellationInterrupt(s);
        }
}

//我再把上面的四种状态流转拿下来
//任务顺利执行:NEW -> COMPLETING -> NORMAL
//任务执行异常:NEW -> COMPLETING -> EXCEPTIONAL
//任务取消:NEW -> CANCELLED
//任务中断:NEW -> INTERRUPTING -> INTERRUPTED
//设置任务的执行结果给outcome属性,并且state从NEW->COMPLETING->NORMAL,唤醒waiter队列中所有节点对应的阻塞线程,删除其队列中的所有节点
protected void set(V v) {
        //首先使用UnSafe的比较内存的旧值,替换成新的值,首先比较FutureTask实例的state属性内存值是否为NEW,如果是就将其替换为完成中COMPLETING的中间状态
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            //将NEW状态替换为COMPLETING状态成功,将其传入进来的返回值赋值给FutureTask实例的outcome属性
            outcome = v;
            //然后将其state的属性值重新设置为NORMAL状态,任务正常结束状态,终态
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
            //删除所有的等待节点,并且唤醒所有节点对应的线程,详细的介绍可以看下面对finishCompletion方法的介绍 
            finishCompletion();
        }
}

//设置任务的执行异常给outcome属性,并且属性state从NEW->COMPLETING->EXCEPTIONAL,唤醒waiter队列中所有节点对应的阻塞线程,删除其队列中的所有节点
protected void setException(Throwable t) {
        //首先使用UnSafe实例比较FutureTask实例的state属性的内存旧值是否是NEW值,如果是将其内存重新赋值为COMPLETING状态值
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            //如果从内存中将NEW状态值替换为COMPLETING状态成功,将其执行任务中出现的异常对象赋值给outcome对象 
            outcome = t;
            //然后将其state的属性值重新设置为EXCEPTIONAL状态值,任务执行出现异常,终态
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);
            //删除所有的等待节点,并且唤醒所有节点对应的线程,详细的介绍可以看下面对finishCompletion方法的介绍 
            finishCompletion();
        }
}   

//如果有线程调用FutureTask任务的cancel方法,并且执行任务的线程还未调用interrupt方法,此时的state的状态为INTERRUPTING状态值
private void handlePossibleCancellationInterrupt(int s) {
        //如果FutureTask的属性state状态值是INTERRUPTING值
        if (s == INTERRUPTING)
            //如果当前任务的状态还是INTERRUPTING,让其执行任务的线程让步,让出cpu,循环等到cancel方法将其state属性更改为INTERRUPTED
            while (state == INTERRUPTING)
                //线程让步
                Thread.yield();

}

//在任务执行完成(包括任务取消、正常结束、发生异常),将等待队列置空,唤醒其所有等待队列中的所有节点对应的阻塞线程,同时让任务执行体置空
private void finishCompletion() {
        //从等待队列的头结点开始
        for (WaitNode q; (q = waiters) != null;) {
            //使用UnSafe的cas将其FutureTask的waiters的等待队列置为空
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                //循环唤醒等待队列节点对应的线程
                for (;;) {
                    //获取节点对应的线程
                    Thread t = q.thread;
                    //如果线程不为空
                    if (t != null) {
                        //将节点对应的thread属性线程置为空
                        q.thread = null;
                        //使用LockSupport唤醒线程,Lock的底层也是使用LockSupport进行线程的阻塞和唤醒
                        LockSupport.unpark(t);
                    }
                    //获取当前节点的下一个节点
                    WaitNode next = q.next;
                    //如果下一节点为空,表明队列中没有其余节点,退出循环
                    if (next == null)
                        break;
                    //将当前节点的下一节点置为空,加快gc
                    q.next = null; // unlink to help gc
                    //将下一节点设置为当前节点 
                    q = next;
                }
                //退出循环
                break;
            }
        }
        //预留下的模板方法,如果有需要在任务执行完成,不管是任务取消、正常完成、还是出现异常,做一些处理,可以自定义一个类实现FutureTask类,重写此done方法    
        done();
        //任务执行体置空
        callable = null;
}

//FutureTask预留下的模板方法,可以自定义子类进行实现
protected void done() { }

//相对上面介绍的run方法,此方法不会将任务正常的执行结果赋值给outcome属性,即不会调用上面介绍的set方法,将callable任务置为空,也不会修改FutureTask的状态,为此任务可以重复被执行,详细的看上面的介绍,但是如果任务执行出现异常,还会调用上面介绍的setException方法,详细的可以看上面setException方法的介绍
//如果想要使用此方法,因为是protect,可以写个类继承于FutureTask类,这样的话任务可以重复的被执行,但是有个问题是获取不到任务的结果,如果是需要重复被执行,并且不需要拿到其结果
protected boolean runAndReset() {
        //如果状态不是初始化状态,直接返回,避免重复执行,或者使用UnSafe的cas将FutureTask实例的runner属性设置为当前线程,即将要执行该任务的线程,如果设置失败也直接返回,表明此任务有线程正在执行,避免重复执行
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            //状态不是NEW或者设置执行线程失败,直接退出
            return false;
        //设置初始的执行状态为false
        boolean ran = false;
        //将FutureTask的属性state赋值给局部变量s
        int s = state;
        try {
            //传入进来的callable对象赋值给局部变量c
            Callable<V> c = callable;
            //传入进来的callable对象不为空并且FutureTask的状态为NEW 
            if (c != null && s == NEW) {
                try {
                    //调用callable类的call方法
                    c.call(); // don't set result
                    //如果callable类的call方法正常执行,将其ran标志位设置为true
                    ran = true;
                } catch (Throwable ex) {
                    //FutureTask实例状态的推进,以及将异常的对象赋值给FutureTask的outcome属性,看上面setException方法的详细介绍
                    setException(ex);
                }
            }
        } finally {
            //最后将记录执行当前任务的线程的FutureTask的属性runner置为空
            runner = null;
            //获取当前FutureTask实例的属性state状态值 
            s = state;
            //如果当前状态属于中断中,或者已中断,执行handlePossibleCancellationInterrupt方法
            if (s >= INTERRUPTING)
                //看上面handlePossibleCancellationInterrupt方法的介绍
                handlePossibleCancellationInterrupt(s);
        }
        //因为runAndReset方法不会修改FutureTask的状态,如果当前任务正常执行完成并且当前FutureTask的属性state状态为NEW状态值
        return ran && s == NEW;
}

//判断FutureTask任务是否已经取消,state不管是取消、还是中断中、已中断,任务都是已取消
public boolean isCancelled() {
    //如果FutureTask的属性state状态值大于等于CANCELLED,不管任务是被取消还是中断(INTERRUPTEING、INTERRUPTED值)
    return state >= CANCELLED;
}

//判断FutureTask任务是否已经完成,state不是NEW初始化状态,任务都是已完成
public boolean isDone() {
    //state不等于初始化状态
    return state != NEW;
}

//取消当前任务,如果mayInterruptIfRunning参数是true的话,此方法是中断当前任务,不管取消当前任务还是中断当前任务,只有FutureTask任务的属性state为NEW,才能取消、中断任务
public boolean cancel(boolean mayInterruptIfRunning) {
        //如果当前任务的状态state不是NEW状态值,或者根据mayInterruptIfRunning参数,使用UnSafe的cas将状态NEW替换为INTERRUPTING或者CANCELLED状态值失败,直接退出 
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {
            //如果传入进来的mayInterruptIfRunning的标志位是true,表明要中断执行任务的线程
            if (mayInterruptIfRunning) {
                try {
                    //获取执行任务的线程
                    Thread t = runner;
                    //执行任务的线程不为空
                    if (t != null)
                        //中断执行任务的线程
                        t.interrupt();
                } finally { // final state
                    //将FutureTask的状态state设置为INTERRUPTED,表明FutureTask的状态为已中断
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            //在任务执行完成(包括任务取消、正常结束、发生异常),将等待队列置空,唤醒其所有等待队列中的所有节点对应的阻塞线程,同时让任务执行体置空,详细的介绍可以看上面对finishCompletion方法的介绍
           finishCompletion();
        }
        //根据mayInterruptIfRunning参数取消或者中断任务成功
        return true;
}

//其他线程调用FutureTask任务的get方法获取任务的执行结果,比如main线程调用t线程执行的任务FutureTask的get方法,如果没法马上获取到值,阻塞到任务执行完成,get方法可能抛出执行异常,中断异常、或者取消异常    
public V get() throws InterruptedException, ExecutionException {
        //获取FutureTask的属性state值
        int s = state;
        //如果当前状态小于等于COMPLETING,调用下面介绍的awaitDone方法
        if (s <= COMPLETING) 
            //awaitDone方法看下面对此方法的介绍
            s = awaitDone(false, 0L);
        //根据任务执行完成的状态,判断是直接返回结果,还是抛出执行异常,或者取消异常,看下面对此report方法的介绍
        return report(s);
}
    
//和上面get方法一样,只是此方法是支持超时获取结果,如果在超时FutureTask的状态还是小于等于COMPLETING,则会抛出超时异常,还会有其他异常中断异常、执行异常、取消异常    
public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        //如果传入unit是空,抛出空指针异常
        if (unit == null)
            throw new NullPointerException();
        //获取FutureTask的属性state值 
        int s = state;
        //如果当前状态小于等于COMPLETING,并且调用awaitDone()方法,超时,状态还是COMPLETING,抛出超时异常
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        //根据任务执行完成的状态,判断是直接返回结果,还是抛出执行异常,或者取消异常,看下面对此report方法的介绍
        return report(s);
}    

//等待任务完成,返回任务执行完成的状态
private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        //如果传入的timed是true支持超时,deadline为当前时间加上超时时间,或者阻塞直到唤醒,即调用LockSupport.unpark方法
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        //将其要阻塞的当前节点设置为等待队列的起始节点的标志位
        boolean queued = false;
        for (;;) {
            //如果当前要阻塞的线程,被中断 
            if (Thread.interrupted()) {
                //移除当前节点,看下面removeWaiter方法的介绍
                removeWaiter(q);
                //抛出中断异常
                throw new InterruptedException();
            }
            //获取FutureTask的属性state值
            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)
                //将其当前要阻塞的节点设置为等待队列的起始节点,下一节点为上一次节点的起始节点
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            //如果当前传入进来的标志位timed是true,即支持超时
            else if (timed) {
                //得到要阻塞的时间 
                nanos = deadline - System.nanoTime(); 
                //如果当前要阻塞的超时时间小于等于0,表明已超时
                if (nanos <= 0L) {
                    //移除当前要等待的节点
                    removeWaiter(q);
                    //返回状态 
                    return state;
                }
                //超时的阻塞当前要获取任务结果的线程
                LockSupport.parkNanos(this, nanos);
            }
            else
                //否则的话阻塞当前要获取任务结果的线程,除非调用LockSupport.unpark方法才能唤醒
                LockSupport.park(this);
        }
}

@SuppressWarnings("unchecked")
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        //如果当前传入进来的状态是NORMAL,即任务正常执行完成,返回结果
        if (s == NORMAL)
            //返回任务正常执行完成的结果
            return (V)x;
        //如果当前传入进来的状态是已取消或者被中断中、已中断
        if (s >= CANCELLED)
            //抛出取消异常
            throw new CancellationException();
        //否则抛出执行异常 
        throw new ExecutionException((Throwable)x);
}

//移除传入的节点,如果有多个线程调用FutureTask的get方法,队列有很多节点,会影响性能
private void removeWaiter(WaitNode node) {
        //如果传入的节点不为空
        if (node != null) {
            //将其传入节点的对应属性thread线程置为空
            node.thread = null;
            retry:
            //重新开始移除节点
            for (;;) {          // restart on removeWaiter race
                //从FutureTask队列的起始节点开始,如果节点不为空,开始循环
                for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                    //获取当前节点的下一节点
                    s = q.next;
                    //如果当前节点对应的线程不为空
                    if (q.thread != null)
                        //将其当前节点设置为前置节点
                        pred = q;
                    //如果当前节点对应的线程为空,将其当前节点的下一节点设置为前置节点,即上一个节点,移除当前节点
                    else if (pred != null) {
                        pred.next = s;
                        //如果上一次节点的线程也被置为空,重新循环移除节点
                        if (pred.thread == null) // check for race
                            continue retry;
                    }
                    //只有两个节点,将当前节点的下一节点设置为队列的起始节点
                    else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                          q, s))
                        //设置失败重新循环获取节点
                        continue retry;
                }
                //退出循环
                break;
            }
        }
}
复制代码


转载于:https://juejin.im/post/5d08be8ce51d455d6c0ad925

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FutureTask是一个实现了RunnableFuture接口的类,它继承了Runnable和Future接口。因此,FutureTask既可以被当作一个Runnable来使用,也可以被当作一个Future来使用。 FutureTask实现了Future接口,完成了对Future接口的基本实现。除了实现了Future接口以外,FutureTask还实现了Runnable接口,这意味着它可以交由Executor执行,也可以直接用线程调用执行(futureTask.run())。 FutureTask具有三种执行状态:未启动、已启动和已完成。未启动指的是在调用run()方法之前,FutureTask处于未启动状态。已启动指的是FutureTask对象的run方法正在执行过程中,FutureTask处于已启动状态。已完成指的是FutureTask正常执行结束,或者被取消,或者执行过程中抛出异常而导致中断而结束。 在ThreadPoolExecutor的submit方法中,返回的是一个Future的实现,而这个实现就是FutureTask的一个具体实例。FutureTask帮助实现了具体的任务执行,并与Future接口中的get方法关联起来。 总结起来,FutureTask是一个能够同时担任Runnable和Future角色的类,它可以作为一个任务提交给线程池执行,也可以通过get方法来获取任务执行的结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【第十二篇】Java 线程池FutureFutureTask【重点】](https://blog.csdn.net/weixin_42039228/article/details/123198358)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [FutureTask简介](https://blog.csdn.net/u014516601/article/details/125123415)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值