线程池future实现的基本原理以及实现自己的submit方法

昨天有幸看书的时候看了一下netty的chnnelfuture,其原理感觉应该跟java的callable实现的原理应该差不多,既然这样我们先将java的future先弄个明白,java的callable能通过future拿到返回值,虽然以前也知道有这么一回事但并没有过多的关注,这次正好看一下源码来搞明白其中的奥妙 首先看下面一段示意的代码.

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

    ExecutorService executor = Executors.newSingleThreadExecutor(); final Future<Integer> future = executor.submit(new AddTask(1, 2)); //利用线程池获取future
   
 Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                future.get(); //异步获取值
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    });
    thread.setName("thread-future");
    thread.start();
    Integer result = future.get(); //异步获取值
}

既然是从submit开始我们就从这里开始分析

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task); //注意这里
    execute(ftask); //标准的线程运行方式
    return ftask;  //返回该对象,也就是submit的返回值? 
}

让我们追究一下ftask是怎么来的 往下看 实现如下

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

看到这里你大概明白了吧,返回的是一个FutureTask 其中futureTask 实现了runnable和callable接口,其实看了源码就会明白 本质上也是运行其run 方法哦,然后在run方法中调用call方法,并用一个成员变量保持call方法的返回值

所以接下来我们看一看futureTask的run方法

public void run() {
    if (state != NEW ||             //一些状态的判断,我们看主体 暂时忽略
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable; //这里注意哦 ,我们传进来的callable接口
        if (c != null && state == NEW) {
            V result;
            boolean ran; //判断call是否正常执行,也就是异常,中断啥的
            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 = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

//好了 我们大概知道了future执行的原理,让我们来看看别的线程调用future.get()会发生什么 再次之前,我们需要先补充future中关于执行状态的抽象描述

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; //中断完成

//状态只有这四种走向,并且线程一段开启,就不能被取消,只能中断

NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTIONAL
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED

好了有了这些我们看看future.get发生了什么 ,

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING) //还没有完成
        s = awaitDone(false, 0L); //进入这个函数
    return report(s);
}

看看awaitDonn,有什么玄机呢,从方法命名也可以看出,等待完成

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(); //创建一个等待节点 超级简单的 源码看下面
        else if (!queued)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                            q.next = waiters, q); //利用CAS入对,失败下次自旋会继续进入这里
        else if (timed) {
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos); 
        }
        else
            LockSupport.park(this); //阻塞线程
    }
}

 

我们先看waitNode的源码 一个超级简单的类

static final class WaitNode {
    volatile Thread thread; //当前线程的引用
    volatile WaitNode next; //指向下一个,从而将等待的线程全部连起来
    WaitNode() { thread = Thread.currentThread(); }
}

看看上面的awaitDone方法木有,通过一个无限for循环和CAS机制,来将所调用future.get的线程在没有完成之前全部阻塞掉,并且阻塞线程通过一个next指针互相关联,既然阻塞了所有线程,那么什么时候唤醒了?显然唤醒的时机就是FutureTask的run方法执行完的时机,往前面的futureTask的run方法中有一个set方法 让我们来看看个究竟

protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v; 
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion(); //这里关键,任务完成 
    }
}

finishCompletion()方法,没错线程就是在这里面实现唤醒了 继续看源码

private void finishCompletion() {
    // assert state > COMPLETING;
    for (WaitNode q; (q = waiters) != null;) { //获取waiters节点,也就是头节点
      //确保只有一个线程能够执行唤醒操作,以确保唤醒的正确进行
        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; //将q执行先一个节点
            }
            break;
        }
    }

    done();

    callable = null;        // to reduce footprint
}

看上面我每行的注释应该已经说的很明白了,也就是将等待的节点从头到尾一个一个的唤醒,从而获取到结果

至此future的原理,已经讲完。最后不得不感叹,大神还是大神,不过只要我们坚持不懈的努力最终也会变成大神的,最后为了大家在一次深刻的理解future的原理,我们不适用线程池的submit方法而是自己写一个简易的submit方法来加深大家的印象,具体的实现方式如下,

package com.ACM;

import java.util.concurrent.*;

/**
 * @author zoujianglin
 * @date 2018/8/8 12:53
 */
public class OtherSolution {

    private volatile Object result;


    public static RunnableFuture sumbit(Callable callable) {

        RunnableFuture ftask = newTaskFor(callable);
        Thread thread = new Thread(ftask);
        thread.start(); //开启线程 运行ftask的run方法,并返回ftask
        return ftask;
    }

    protected static RunnableFuture newTaskFor(Callable callable) {
        return new FutureTask(callable);
    }

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

        final Future future = sumbit(new AddTask(1, 2)); //得到future
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    future.get(); //异步获取future结果
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        });
        thread.setName("thread-future");
        thread.start();
        Integer result = (Integer) future.get(); //异步获取future的结果
        System.out.println(result);


        //  int[] gas = new int[]{1, 2};
        //int[] cost = new int[]{2, 1};
    }


    static class AddTask implements Callable {

        private int a, b;

        public AddTask(int a, int b) {
            this.a = a;
            this.b = b;
        }

        @Override
        public Integer call() throws Exception {
            Integer result = a + b;
            return result;
        }
    }


}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值