ForkJoinPool 源码分析

public ForkJoinPool() {
    // MAX_CAP      = 0x7fff  32767; 所以最大线程数。默认就是CPU核心数
    this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
         defaultForkJoinWorkerThreadFactory, null, false);
}
public ForkJoinPool(int parallelism,
                    ForkJoinWorkerThreadFactory factory, // 线程工厂
                    UncaughtExceptionHandler handler, // 线程异常处理器
                    boolean asyncMode) { // 执行模式:异步或者同步。默认是false
    this(checkParallelism(parallelism), // 非法参数校验
         checkFactory(factory),
         handler,
         asyncMode ? FIFO_QUEUE : LIFO_QUEUE, // 取任务的方式:base和top
         "ForkJoinPool-" + nextPoolId() + "-worker-");
    checkPermission();
}
// 最终构造器
private ForkJoinPool(int parallelism,
                     ForkJoinWorkerThreadFactory factory,
                     UncaughtExceptionHandler handler,
                     int mode,
                     String workerNamePrefix) {
    this.workerNamePrefix = workerNamePrefix;
    this.factory = factory;
    this.ueh = handler;
    this.config = (parallelism & SMASK) | mode; // SMASK = 0x0000 ffff  将config的低16位用于存放并行度
    // static final int MODE_MASK    = 0xffff << 16;  // 高16位保存mode
    // static final int LIFO_QUEUE   = 0; // 栈结构不需要占用位
    // static final int FIFO_QUEUE   = 1 << 16;
    long np = (long)(-parallelism); //以并行度64为例 AC 1111111111000000   TC: 1111111111000000
    this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}

// 将任务封装成FJT,因为FJP只执行FJT的任务
public ForkJoinTask<?> submit(Runnable task) {
        ForkJoinTask<?> job;
        if (task instanceof ForkJoinTask<?>)
            job = (ForkJoinTask<?>) task;
        else
            job = new ForkJoinTask.AdaptedRunnableAction(task);
        externalPush(job); // 核心提交任务方法
        return job;
}
final void externalPush(ForkJoinTask<?> task) {
      externalSubmit(task); // 核心方法
}
// 真正执行提交任务的方法
private void externalSubmit(ForkJoinTask<?> task) {
    int r;                                    // 随机数
    // 取随机数
    for (;;) { // 如果出现这种死循环,必然会有多个判断条件,多个执行分支
        WorkQueue[] ws; WorkQueue q; int rs, m, k;
        boolean move = false;
        if ((rs = runState) < 0) { // 判断线程池是否已经关闭
            tryTerminate(false, false);  
            throw new RejectedExecutionException();
        }
        else if ((rs & STARTED) == 0 || // STARTED状态位没有置位,所以需要初始化
                 ((ws = workQueues) == null || (m = ws.length - 1) < 0)) { // workQueues需要初始化
            // 注意:如果该分支不执行,但是由于||运算符的存在,这里ws和m变量已经初始化
            int ns = 0;
            rs = lockRunState();
            try {
                if ((rs & STARTED) == 0) { // 再次判断了一下STARTED状态位,为何?因为多线程操作,可能上面判断过后,已经被别的线程初始化了,所以没必要再进行CAS
                    U.compareAndSwapObject(this, STEALCOUNTER, null,
                                           new AtomicLong());
                    // p就是并行度:工作线程的数量
                    int p = config & SMASK; // ensure at least 4 slots
                    int n = (p > 1) ? p - 1 : 1; // n最小为1
                    // 就是取传入的数,最近的2的倍数
                    1 |= n >>> 1; n |= n >>> 2;  n |= n >>> 4;
                    n |= n >>> 8; n |= n >>> 16; n = (n + 1) << 1;
                    workQueues = new WorkQueue[n]; // 初始化workQueue队列(外部提交队列+工作窃取队列)
                    ns = STARTED; // 初始化成功,那么此时状态修改为STARTED状态
                }
            } finally {
                unlockRunState(rs, (rs & ~RSLOCK) | ns); // 最后释放锁
            }
        }
        // 在上一个分支执行完毕后,第二次循环将会到达这里。由于咱们的算法是用了一个全局队列:workQueues来存储两个队列:外部提交队列、内部工作队列(任务窃取队列),那么这时,是不是应该去找到我这个任务放在哪个外部提交队列里面。就是通过上面获取的随机种子r,来找到应该放在哪里?SQMASK = 1111110,所以由SQMASK的前面的1来限定长度,末尾的0来表明,外部提交队列一定在偶数位
        else if ((q = ws[k = r & m & SQMASK]) != null) {
            // 由于当前提交队列是外部提交队列,那么一定会有多线程共同操作,那么为了保证并发安全,那么这里需要上锁,也即对当前提交队列进行锁定
            if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
                ForkJoinTask<?>[] a = q.array; // 取提交队列的保存任务的数组array
                int s = q.top; // doug lea 大爷发现,从top往下放,类似于压栈过程,而栈顶指针就叫sp(stack pointer),所以简写为s
                boolean submitted = false; // initial submission or resizing
                try {                      // locked version of push
                    if (
                        (
                        a != null && // 当前任务数组已经初始化
                            a.length > s + 1 - q.base // 判断数组是否已经满了
                        ) 
                        ||
                        (a = q.growArray()) != null) { // 那么就初始化数组或者扩容
                        // 扩容之后,开始存放数据
                        int j = (((a.length - 1) & s) << ASHIFT) + ABASE; // j就是s也就是栈顶的绝对地址
                        U.putOrderedObject(a, j, task); // 放数据
                        U.putOrderedInt(q, QTOP, s + 1); // 对栈顶指针+1
                        // 为什么这里需要怎么写?注意:qtop和q.array没有volatile修饰
                        submitted = true;
                    }
                } finally {
                    U.compareAndSwapInt(q, QLOCK, 1, 0); // qlock是volatile的,由于volatile的特性,这个操作CAS出去,那么qlock线程可见,必然上面的task和qtop可见,且有序。
                }
                // 由于添加成功了,但是,没有工作线程,那么这时通过signalWork,创建工作线程并执行
                if (submitted) {
                    signalWork(ws, q);
                    return;
                }
            }
            move = true; // 由于当前线程无法获取初始计算的提交队列的锁,那么这时发生了线程竞争,那么设置move标志位,让线程在下一次循环的时候,重新计算随机数,让它寻找另外的队列。
        }
        else if (((rs = runState) & RSLOCK) == 0) { // 如果找到的这个wq没有被创建,那么创建他,但是,这里的RSLOCK的判断,在于,当没有别的线程持有RSLOCK的时候,才会进入。这是由于RSLOCK主管,runstate,可能有别的线程把状态改了,根本不需要再继续work了
            q = new WorkQueue(this, null); // 创建外部提交队列,由于ForkJoinWorkerThread FJWT为null,所以为外部提交队列
            q.hint = r; // r为什么保存为hint,r是随机数,通过r找到当前外部提交队列,处于WQS的索引下标
            q.config = k | SHARED_QUEUE; // SHARED_QUEUE = 1 << 31;这里就是将整形int的符号位置1,所以为负数,SHARED_QUEUE表明当前队列是共享队列(外部提交队列)。而k为当前wq处于wqs中的索引下标
            q.scanState = INACTIVE; // 由于当前wq并没有进行扫描任务,所以扫描状态位无效状态INACTIVE
            rs = lockRunState();           // 对wqs上锁操作:就是讲上面的队列放入到wqs的偶数位中
            if (rs > 0 &&  // 确保线程池处于运行状态
                (ws = workQueues) != null &&
                k < ws.length && ws[k] == null) // 由于可能两个线程同时进来操作,只有一个线程持有锁,那么只允许一个线程放创建的队列,但是这里需要注意的是:可能会有多个线程创建了WorkQueue,但是只有一个能成功
                ws[k] = q;     // 将wq放入全局队列wqs中
            unlockRunState(rs, rs & ~RSLOCK); // 解锁
        }
        else // 发生竞争时,让当前线程选取其他的wq来重试
            move = true;                 
        if (move)
            r = ThreadLocalRandom.advanceProbe(r); // 获取下一个不同的随机数
    }
}
// 上面的内容,都是初始化,放,but,执行线程没有啊,谁来执行这个放入到了外部提交队列中的任务。
final void signalWork(WorkQueue[] ws, WorkQueue q) {
    long c; int sp, i; WorkQueue v; Thread p;
    while ((c = ctl) < 0L) {                       // 符号位没有溢出:最高16位为AC,代表了工作活跃的线程数没有达到最大值
        if ((sp = (int)c) == 0) {                  // ctl的低32位代表了INACTIVE数。若此时sp=0,代表了没有空闲线程
            if ((c & ADD_WORKER) != 0L)            // ADD_WORKER = 0x0001L << (TC_SHIFT + 15) TC的最高位是不是0,若不是0,那么FJP中的工作线程代表了没有达到最大线程数
                tryAddWorker(c); // 尝试添加工作线程
            break;
        }
        // 此时,代表了空闲线程不为null
        if (ws == null)                            // 此时FJP的状态为NOT STARTED,TERMINATED
            break;
        if (ws.length <= (i = sp & SMASK))         // SMASK = 0xffff 取sp的低16位 TERMINATED
            break;
        if ((v = ws[i]) == null)                   // ctl低32位的低16位是不是存放了INACTIVE线程在wqs的下标i  TERMINATING
            break;
        int vs = (sp + SS_SEQ) & ~INACTIVE;        // SS_SEQ = 1 << 16; ctl低32位的高16位是不是存放了版本计数 version count   INACTIVE= 1 << 31;
        int d = sp - v.scanState;                  // screen CAS
        long nc = (UC_MASK & (c + AC_UNIT)) | // 把获取到的INACTIVE的线程,也即空闲线程唤醒,那么唤醒后,是不是应该对AC + 1(加1操作(c + AC_UNIT)),UC_MASK为高32位1,低32位0,所以(UC_MASK & (c + AC_UNIT))代表了,保留ctl的高32位值,也即AC+1和TC值
            (SP_MASK & v.stackPred); // SP_MASK为高32位0,低32位1,所以保留了低32位的值,v.stackPred 代表了一个出栈操作,让低32位的低16位更新为唤醒线程的下一个线程
        // 此时的nc就是计算好的下一个ctl,next ctl -> nc
        if (d == 0 && U.compareAndSwapLong(this, CTL, c, nc)) { // CAS 替换CTL
            v.scanState = vs;           // 记录版本信息放入scanState,此时为正数
            if ((p = v.parker) != null) // 唤醒工作线程
                U.unpark(p);
            break;
        }
        if (q != null && q.base == q.top)    // 队列为空,直接退出。由于队列是多线程并发的,所以有可能放入其中的任务已经被其他线程获取,所以此时队列为空
            break;
    }
}
// 尝试添加工作线程
private void tryAddWorker(long c) {
    boolean add = false;
    do {
        long nc = ((AC_MASK & (c + AC_UNIT)) | // active count +1 活跃线程数加1,此时只保留了高32位的高16位信息
                   (TC_MASK & (c + TC_UNIT))); // total count +1 总线程数加1,此时只保留了高32位的低16位信息
        // 此时nc为next ctl,也即活跃线程数+1,总线程数+1
        if (ctl == c) { // ctl没有被其他线程改变
            int rs, stop;           
            // 上锁并检查FJP的状态是否为STOP
            if ((stop = (rs = lockRunState()) & STOP) == 0) 
                add = U.compareAndSwapLong(this, CTL, c, nc); // 更新ctl的值。add表明是否添加成功
            unlockRunState(rs, rs & ~RSLOCK); // 解锁
            if (stop != 0) // 线程池已经停止
                break;
            if (add) { // 如果ac和tc添加1成功,也即nc替换成功,那么创建工作线程
                createWorker();
                break;
            }
        }
    } while (((c = ctl) & ADD_WORKER) != 0L  // 没有达到最大线程数
             && (int)c == 0); // 低32位0,如果有空闲线程,你添加他干甚?
}
// 创建工作线程
private boolean createWorker() {
    ForkJoinWorkerThreadFactory fac = factory;
    Throwable ex = null;
    ForkJoinWorkerThread wt = null;
    try {
        if (fac != null && (wt = fac.newThread(this)) != null) { // 从线程工厂中,创建线程
            wt.start(); // 并启动线程
            return true;
        }
    } catch (Throwable rex) {
        ex = rex;
    }
    deregisterWorker(wt, ex); // 如果创建出现异常,将ctl前面加的1回滚
    return false;
}
// 默认线程工厂
static final class DefaultForkJoinWorkerThreadFactory
    implements ForkJoinWorkerThreadFactory {
    public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
        return new ForkJoinWorkerThread(pool); // 直接new ForkJoinWorkerThread
    }
}
// FJWT的构造器
protected ForkJoinWorkerThread(ForkJoinPool pool) {
    super("aForkJoinWorkerThread");
    this.pool = pool; // 保存对外的FJP的引用 
    this.workQueue = pool.registerWorker(this); // 将自己注册到FJP中,其实就是保存到FJP的奇数位中
}
// 将自己注册到FJP中
final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
    UncaughtExceptionHandler handler;
    wt.setDaemon(true);                           //  FJWT的守护状态为守护线程
    if ((handler = ueh) != null) // 如果设置了线程的异常处理器,那么设置
        wt.setUncaughtExceptionHandler(handler);
    WorkQueue w = new WorkQueue(this, wt); // 创建工作线程,注意:创建外部提交队列时: WorkQueue w = new WorkQueue(this, null)
    int i = 0;                                    // 创建的工作队列所保存在wqs的索引下标
    int mode = config & MODE_MASK;                // 取设置的工作模式:FIFO、LIFO
    int rs = lockRunState(); // 上锁
    try {
        WorkQueue[] ws; int n;                    
        if ((ws = workQueues) != null && (n = ws.length) > 0) { // 日常判空操作
            int s = indexSeed += SEED_INCREMENT;  // indexSeed = 0 SEED_INCREMENT = 0x9e3779b9 ,减少hash碰撞
            int m = n - 1; // wqs长度-1,用于取模运算
            i = ((s << 1) | 1) & m;               // 找存放w的下标
            if (ws[i] != null) {                  // 发生了碰撞
                int probes = 0;                   // step by approx half n
                int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2; // 发生碰撞二次寻址 EVENMASK     = 0xfffe;
                while (ws[i = (i + step) & m] != null) { // step保证为偶数,一个奇数+偶数一定等于奇数
                    if (++probes >= n) { // 寻址达到了极限,那么扩容
                        workQueues = ws = Arrays.copyOf(ws, n <<= 1); // 扩容容量为2倍
                        m = n - 1;
                        probes = 0;
                    }
                }
            }
            w.hint = s;                           // s作为随机数保存在wq的hint中
            w.config = i | mode;                  // 保存索引下标 + 模式
            w.scanState = i;                      // scanState为volatile,此时对它进行写操作,ss写成功,上面的变量一定可见,且不会和下面的ws[i]赋值发生重排序。注意这里的scanState就变成了odd,也即奇数,所以要开始扫描获取任务并执行啦
            ws[i] = w; // 放入全局队列中
        }
    } finally {
        unlockRunState(rs, rs & ~RSLOCK); // 解锁
    }
    wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
    return w;
}
// FJWT的run方法,执行体
public void run() {
    if (workQueue.array == null) { // 只run一次。array用于存放FJT,那么这里用它作为标识,来确保FJWT只run一次
        Throwable exception = null;
        try {
            onStart(); // run之前执行钩子函数
            pool.runWorker(workQueue); // 核心方法
        } catch (Throwable ex) {
            exception = ex;
        } finally {
            try {
                onTermination(exception);  // run之后执行钩子函数
            } catch (Throwable ex) {
                if (exception == null) // 如果异常为空,则保存这里的异常,否则异常为上面的catch块异常。其实呢就是一句话:保存最先发生的异常
                    exception = ex;
            } finally {
                pool.deregisterWorker(this, exception);  // 线程退出后,进行状态还原
            }
        }
    }
}
// FJP中的真正处理FJWT工作的函数
final void runWorker(WorkQueue w) {
    w.growArray();                   // 分配array
    int seed = w.hint;               // 取随机数
    int r = (seed == 0) ? 1 : seed;  // 避免出现0
    for (ForkJoinTask<?> t;;) { // 循环获取任务并执行,直到显示退出
        if ((t = scan(w, r)) != null) // 扫描可执行任务
            w.runTask(t); // 拿到任务之后开始执行
        else if (!awaitWork(w, r)) // 如果没有任务可执行,那么awaitWork等待任务执行
            break; // 显式退出
        r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // 异或算法,基于上一个随机数r,计算下一个伪随机数
    }
}
// WorkQueue类中用于初始化或者增长array函数
final ForkJoinTask<?>[] growArray() {
    ForkJoinTask<?>[] oldA = array;
    // oldA存在,那么进行二倍长度扩容,否则size为初始化大小INITIAL_QUEUE_CAPACITY = 1 << 13
    int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
    // 如果扩容之后,超过最大容量MAXIMUM_QUEUE_CAPACITY = 1 << 26;也即64M,抛出异常
    if (size > MAXIMUM_QUEUE_CAPACITY)
        throw new RejectedExecutionException("Queue capacity exceeded");
    int oldMask, t, b;
    // 创建新的array数组
    ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
    // 扩容后,需要干嘛?需要将旧的array中的任务放入到新的数组中
    // 面试题:为什么这里手动复制,而不用更快速的复制?System.arraycopy?面的是什么??如何保证数组中的元素的可见性?getObjectVolatile(数组的首地址,偏移量)
    if (oldA != null && (oldMask = oldA.length - 1) >= 0 &&
        (t = top) - (b = base) > 0) {
        int mask = size - 1;
        do { 
            ForkJoinTask<?> x;
            // 注意:这里从base引用开始取任务
            int oldj = ((b & oldMask) << ASHIFT) + ABASE;
            int j    = ((b &    mask) << ASHIFT) + ABASE;
            x = (ForkJoinTask<?>)U.getObjectVolatile(oldA, oldj); // volatile 语义 1
            if (x != null &&
                U.compareAndSwapObject(oldA, oldj, x, null)) // 这里为何使用CAS?因此由于队列是工作窃取队列,可能有别的线程持有old数组引用,正在通过base引用窃取尾部任务
                U.putObjectVolatile(a, j, x);  // volatile 语义 2
        } while (++b != t); // 循环,直到转移成功
    }
    return a;
}
// FJP中,在FJWT进行扫描获取任务执行。w为当前FJWT所处队列,r为随机数
private ForkJoinTask<?> scan(WorkQueue w, int r) {
    WorkQueue[] ws; int m;
    if ((ws = workQueues) != null && (m = ws.length - 1) > 0 && w != null) { // 日常判空
        int ss = w.scanState;                     // 保存初始时的扫描状态
        // 扫描获取任务。origin初始为随机数取模的下标,k初始为origin。
        // 由于在扫描过程中,可能有别的线程添加获取等等操作,那么我怎么样让当前FJWT返回呢?不可能一直在这扫描吧,实在是没有任务了,那么必须退出,避免造成性能损耗,那么问题就变为:如何发现当前没有可执行的任务呢?(这叫推理学习,记忆方式),所以采用oldSum和checkSum来判断整个wqs是否处于稳定状态,也即没有别的线程再往里添加任务了,而且经历过一个周期,没有扫描到可执行任务,即可退出。
        for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) {
            WorkQueue q; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
            int b, n; long c;
            // 哈哈,在wqs中找到了一个不为空的队列。那么看看有没有可以获取任务
            if ((q = ws[k]) != null) { 
                if ((n = (b = q.base) - q.top) < 0 && // 由于放任务将会操作top指针,初始时,base等于top,每添加一个任务,top加1,所以随着任务的添加,那么此时base落后于top,所以base - top < 0 表明队列有任务
                    (a = q.array) != null) {      // array队列不为空,表明有任务可以获取
                    long i = (((a.length - 1) & b) << ASHIFT) + ABASE; // 取array索引下标base的偏移地址
                    if ((t = ((ForkJoinTask<?>)
                              U.getObjectVolatile(a, i))) != null && // 任务存在
                        q.base == b) { // base引用没有被改变,也即任务没有被取走
                        if (ss >= 0) { // 如果扫描状态正常
                            // CAS取任务
                            if (U.compareAndSwapObject(a, i, t, null)) {
                                q.base = b + 1; // 增加base值
                                if (n < -1)       // 队列大于一个任务,那么其他线程赶紧起来干活
                                    signalWork(ws, q);
                                return t; // 返回获取的任务
                            }
                        }
                        else if (oldSum == 0 &&   
                                 w.scanState < 0) // oldSum未改变之前,才能判断w的扫描状态,如果扫描状态小于0,代表INACITVE,此时需要尝试唤醒空闲线程进行扫描工作
                            // c最新的ctl值,ws[m & (int)c]栈顶的索引下标,AC_UNIT 用于计算活跃线程数
                            tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
                    }
                    // 任务不存在,那么判断,如果此时扫描状态处于INACTIVE的话,那么需要重新获取扫描状态,可能别的线程已经将其置为扫描状态
                    if (ss < 0)      
                        ss = w.scanState;
                    // 执行到这里,那么只可能是因为线程竞争导致的,所以为了减少竞争,那么重新计算随机数,转移获取任务的wq,复位origin,oldSum、checkSum 方便计算轮回
                    r ^= r << 1; r ^= r >>> 3; r ^= r << 10;
                    origin = k = r & m;        
                    oldSum = checkSum = 0;
                    continue;
                }
                checkSum += b; // 通过base数值来进行校验和计算
            }
            // 扫描wqs正好经历过一个周期
            if ((k = (k + 1) & m) == origin) {    // continue until stable
                if ((ss >= 0 || (ss(这时老的) == (ss(这时新的) = w.scanState))) && // 工作线程处于活跃状态,或者状态没有改变,注意:这里进行了ss的状态更新 ss = w.scanState
                    oldSum(这时老的) == (oldSum(这时新的) = checkSum)) { // 旧的oldSum 和 新的 checkSum 比较,同时更新oldSum
                    // 总结: stable 稳定态: 扫描状态不变 且 没有线程操作队列
                    if (ss < 0 || w.qlock < 0)    // 工作线程切换为 INACTIVE且队列稳定,所以退出即可
                        break;
                    int ns = ss | INACTIVE;       // 将扫描状态设置为INACTIVE
                    long nc = ((SP_MASK & ns) |
                               (UC_MASK & ((c = ctl) - AC_UNIT))); // 活跃线程数减1
                    // CTL低32位就是空闲线程栈的栈顶。workqueue的stackPred就是栈中的空闲线程
                    w.stackPred = (int)c;         // 之前栈顶空闲线程的索引下标+版本号
                    // 优化到了极致。由于这里是volatile,进行直接赋值将会导致StoreStore和StoreLoad屏障,所以用unsafe类来普通变量赋值,减少性能损耗,而后面的CAS操作后,自然能够保证这里的scanState语义。
                    U.putInt(w, QSCANSTATE, ns);
                    // CAS 替换ctl值
                    if (U.compareAndSwapLong(this, CTL, c, nc))
                        ss = ns; // 这里又是一个优化点,因为CTL替换成功,必然scanState写成功,那么局部变量直接更新为最新值,而不用再去读scanState变量
                    else
                        w.scanState = ss;         // 如果失败了,那么回退到原来的状态,因为CTL没有改变,也即active count没有减1成功,自然scanState需要回退。问:这里为什么直接写volatile了?
                }
                checkSum = 0;
            }
        }
    }
    return null;
}
// WorkQueue中执行scan获取到的FJT
final void runTask(ForkJoinTask<?> task) {
    if (task != null) { // 日常判空
        scanState &= ~SCANNING; // mark as busy 标记当前wq的工作线程处于执行获取到的任务状态。即标记为偶数。取scanState的高31位。int SCANNING  = 1
        (currentSteal = task).doExec(); // task是不是当前线程从队列里面获取的(scan),也即将task设置为currentSteal。这里先不要了解FJT的内容,只需要知道运行了,怎么运行的。不需要了解怎么去实现FJT等等,后面再说。
        U.putOrderedObject(this, QCURRENTSTEAL, null); // 执行完毕之后释放currentSteal引用。为什么这里这么写?store buffer -> StoreLoad -> volatile语义?写变量时,StoreStore,StoreLoad。那么这里为了保证写入顺序-> putOrderedObject 避免了StoreLoad屏障对性能的损耗。
        // todo
        execLocalTasks(); // 直接翻译:执行本地任务。为何执行本地任务?考虑一个问题:谁能往当前线程的工作队列里放任务?当前线程在执行FJT时往自己队列里放了任务,也只有当前线程才能往array任务数组里放任务。
        ForkJoinWorkerThread thread = owner;
        if (++nsteals < 0)      // nsteals代表了当前线程总的偷取的任务数量。由于符号限制,所以检查是否发生符号溢出
            transferStealCount(pool); // 当前线程32位计数值达到饱和,那么将其加到FJP的全局变量的64位计数器中,并且清零计数值 nsteals
        scanState |= SCANNING; // 任务执行完成,恢复扫描状态
        if (thread != null)
            thread.afterTopLevelExec(); // 任务执行完的钩子函数
    }
}
// FJP用于FJWT工作线程等待唤醒的方法
private boolean awaitWork(WorkQueue w, int r) {
    if (w == null || w.qlock < 0)                 // 线程池正在Terminate
        return false; // 返回false 直接退出runWorker内部循环,也即退出FJWT
    // 取当前工作线程压入空闲栈中的前一个工作线程版本号+下标(32位切割为:高16 + 低16)赋值于pred,SPINS代表自旋次数为0
    for (int pred = w.stackPred, spins = SPINS, ss;;) {
        if ((ss = w.scanState) >= 0) // 当前工作线程状态已经被修改为ACTIVE状态,那么赶紧干活去
            break;
        else if (spins > 0) { // 没有达到自旋次数阈值
            r ^= r << 6; r ^= r >>> 21; r ^= r << 7;
            if (r >= 0 && --spins == 0) {         // randomize spins
                WorkQueue v; WorkQueue[] ws; int s, j; AtomicLong sc;
                if (pred != 0 && (ws = workQueues) != null &&
                    (j = pred & SMASK) < ws.length &&
                    (v = ws[j]) != null &&        // see if pred parking
                    (v.parker == null || v.scanState >= 0))
                    spins = SPINS;                // continue spinning
            }
        }
        else if (w.qlock < 0)                     // 自旋之后再次检测下线程池状态
            return false;
        else if (!Thread.interrupted()) { //  如果当前FJWT工作线程没有发生中断,那么尝试睡眠,否则清除中断标志位后继续scan扫描任务,干活去
            long c, prevctl, parkTime, deadline;
            // 之前我们以并行度64为例 AC(高32位的高16位) 1111 1111 1100 0000  64并行度:0000 0000 0100 0000        
            int ac = (int)((c = ctl) >> AC_SHIFT) +  // AC_SHIFT = 48 符号右移。获取高16位的值:活跃线程数
                (config & SMASK); // 取低16位config的值:在构造函数中设置的并行度(这里是64)。
            //   SMASK = 0xffff
            // 总结:用64位的ctl的高32位的高16位与设置的并行度相加可以得到ac,也即活跃线程数
            // 此时 ac = 0 。那么代表了活跃线程数为0。活跃线程数为0之后,我需要看看FJP有没有设置SHUTDOWN或者STOP。此时需要调用tryTerminate看看自己是不是最后一个线程,然后做清理和进一步的操作。
            if ((ac <= 0 && tryTerminate(false, false)) ||
                (runState & STOP) != 0)           // 线程池状态处于STOP停止态,所以停止执行
                return false;
            // 看看当前线程是不是最后一个空闲线程
            if (ac <= 0 && ss == (int)c) {       
                prevctl = (UC_MASK & (c + AC_UNIT)) | // 为什么这里要加1?我当前线程结束了吗?在我FJP线程代码没有执行完毕之前我还是属于FJP的,所以还是属于活跃线程。这里TC都还没改呢?
                    (SP_MASK & pred); // 因为当前线程要释放资源,不属于FJP了,所以栈顶还原到之前的索引下标
                // 取CTL中的总线程数:活跃线程数(AC)+非活跃线程数(空闲线程数)
                int t = (short)(c >>> TC_SHIFT);  
                // 保证至少有三个空闲线程即可。由于当前线程池处于静默状态,如果持有太多空闲线程将会浪费系统资源,那么何不如把线程释放掉,而又需要保持线程池的活性,即有任务也能不创建线程执行,那么这里设置阈值为3个空闲线程数。这里写法不规范,理论上来说这是可以调节的,设置为全局变量比较合适。
                if (t > 2 &&  // 为什么t>2见上解释
                    U.compareAndSwapLong(this, CTL, c, prevctl)) // 如果这里t>2,那么表明有足够多的空闲线程,那么我需要将CTL还原到 + 1 ac 和 之前栈顶的线程索引。
                    return false;                 
                parkTime = IDLE_TIMEOUT * ((t >= 0) ? 1 : 1 - t);
                deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP;
            }
            else
                prevctl = parkTime = deadline = 0L;
            Thread wt = Thread.currentThread();
            U.putObject(wt, PARKBLOCKER, this);   // 模拟 LockSupport。其实就是设置阻塞对象,表示当前线程阻塞在哪个对象上
            w.parker = wt; // 设置当前workqueue阻塞在哪个线程上
            if (w.scanState < 0 && ctl == c)      // 在执行阻塞之前,再次检测状态位:当前worker必须处于空闲状态且ctl没有被改变过
                U.park(false, parkTime);
            // 被唤醒后,清空标识字段
            U.putOrderedObject(w, QPARKER, null);
            U.putObject(wt, PARKBLOCKER, null);
            // 被唤醒后,扫描状态被设置>0,说明处于活跃状态,直接break
            if (w.scanState >= 0)
                break;
            if (parkTime != 0L && // 设置了阻塞时间
                ctl == c && // ctl没有改变过
                deadline - System.nanoTime() <= 0L && // 超时了
                U.compareAndSwapLong(this, CTL, c, prevctl)) // 收缩线程池
                return false;                    
        }
    }
    return true;
}

// FJP关闭线程池方法
public void shutdown() {
    tryTerminate(false, true);
}
// 实际关闭方法。enable指明如果线程池状态处于活跃状态时,能不能修改状态。
private boolean tryTerminate(boolean now, boolean enable) {
    int rs;
    if (this == common)                       // common公用线程池不允许被关闭
        return false;
    if ((rs = runState) >= 0) {               // 线程池处于活跃状态
        if (!enable) // 如果enable为false,直接退出
            return false;
        rs = lockRunState();                  // 进入 SHUTDOWN 阶段
        unlockRunState(rs, (rs & ~RSLOCK) | SHUTDOWN); // 去除RSLOCK位,并加上SHUTDOWN标志位
    }
    // 此时标志位SHUTDOWN已经设置,那么通过之前的描述得知,此时线程池不会再接收新的任务
    if ((rs & STOP) == 0) { // 此时,线程池状态处于SHUTDOWN状态,没有设置STOP标志位
        if (!now) {                           // 如果没有设置立即无条件结束线程池,那么需要检测一下静默状态,看看是否所有线程都是空闲的
            for (long oldSum = 0L;;) {        // 重复检测,直到FJP稳定
                WorkQueue[] ws; WorkQueue w; int m, b; long c;
                long checkSum = ctl; // 校验和变量默认等于最新的ctl值
                if ((int)(checkSum >> AC_SHIFT) + (config & SMASK) > 0) // 计算ac值
                    return false;             // ac大于0,说明仍然有活跃线程,直接返回即可
                if ((ws = workQueues) == null || (m = ws.length - 1) <= 0)
                    break;                    // wqs都没有创建,那就直接结束呗
                // 遍历所有队列。包括:外部提交队列、内部工作队列(窃取队列)
                for (int i = 0; i <= m; ++i) {
                    if ((w = ws[i]) != null) { // 队列存在
                        if ((b = w.base) != w.top ||  // 队列中有任务
                            w.scanState >= 0 || // 处于工作状态
                            w.currentSteal != null) { // 正在执行任务
                            // 唤醒INACTIVE线程,尽快完成工作
                            tryRelease(c = ctl, ws[m & (int)c], AC_UNIT);
                            return false; // 当前FJP还有任务在执行,当前线程先返回
                        }
                        // 计算校验和
                        checkSum += b;
                        // 取wqs偶数位,设置qlock=-1。禁用外部队列
                        if ((i & 1) == 0)
                            w.qlock = -1;     // try to disable external
                    }
                }
                // 检测FJP是否处于稳定状态,也即没有任务出入,遍历两次。
                if (oldSum == (oldSum = checkSum))
                    break;
            }
        }
        // 当前线程,发现队列处于稳定状态,且已经没有任何任务可执行。直接将状态变为STOP
        if ((runState & STOP) == 0) {
            rs = lockRunState();              // enter STOP phase
            unlockRunState(rs, (rs & ~RSLOCK) | STOP);
        }
    }
	// 此时进入STOP阶段,那么开始帮忙转变状态为terminate
    int pass = 0;                             // 通过三个步骤来逐步帮助terminate线程池
    for (long oldSum = 0L;;) {                // 循环直到完成或者稳定
        WorkQueue[] ws; WorkQueue w; ForkJoinWorkerThread wt; int m;
        long checkSum = ctl;
        // 这是不是最终状态
        if ((short)(checkSum >>> TC_SHIFT) + (config & SMASK) <= 0 || // 总线程数为0,也即FJP中没有活动和非活动线程,也即所有的线程都没了
            (ws = workQueues) == null || (m = ws.length - 1) <= 0) { // 队列不存在
            // 当前线程直接转变状态即可
            if ((runState & TERMINATED) == 0) {
                rs = lockRunState();          // done
                // 将状态改为最终状态:TERMINATED
                unlockRunState(rs, (rs & ~RSLOCK) | TERMINATED);
                // 从这里立刻得知:awaitTermination,等待线程池终结是通过this对象进行阻塞
                synchronized (this) { notifyAll(); } // for awaitTermination
            }
            break;
        }
        // 有线程且队列存在。那么,遍历每一个wq。只处理wq存在的队列。STOP状态含义:工作线程停止工作不管队列中是否还有任务。
        for (int i = 0; i <= m; ++i) {
            if ((w = ws[i]) != null) {
                checkSum += w.base; // 计算校验和,判定队列处于稳定状态,不能放也不能取。
                w.qlock = -1;                 // 直接禁用掉所有的队列wq。此时,不管队列里面是否有任务,都不在执行,详情请看scan方法
                // pass为0时,为第一次进入,所以不会执行下面的语句
                if (pass > 0) {
                    w.cancelAll();            // 将队列中剩余的任务都清空              
                    if (pass > 1 && (wt = w.owner) != null) { // 只有内部工作队列
                          // 如果队列中线程还处于运行状态,那么将其中断
                        if (!wt.isInterrupted()) {
                            try {             // unblock join
                                wt.interrupt();
                            } catch (Throwable ignore) {
                            }
                        }
                        // 如果线程处于INACTIVE状态,那么将其唤醒
                        if (w.scanState < 0)
                            U.unpark(wt);     // wake up
                    }
                }
            }
        }
        // checksum由上面的w.base来决定,如果仍有队列不为空,这时checksum会改变,导致处于不稳定状态,那么重置pass继续循环
        if (checkSum != oldSum) {         
            oldSum = checkSum;
            pass = 0;
        }
        // 到达这里的判断,也即所有的队列都处于稳定状态
        else if (pass > 3 && pass > m)        // 如果当前线程已经经过三次循环,还没有满足第一个判断句,也即总线程数为0,那么看看循环次数是否大于wqs的长度,如果是,那么直接退出,否则继续
            break;
        else if (++pass > 1) {                // 尝试将所有在等待栈中的线程全部唤醒
            long c; int j = 0, sp;           
            while (j++ <= m && (sp = (int)(c = ctl)) != 0)
                tryRelease(c, ws[sp & m], AC_UNIT);
        }
    }
    return true;
}
// 等待FJP线程状态变为TERMINATED
public boolean awaitTermination(long timeout, TimeUnit unit)
    throws InterruptedException {
    // 响应中断
    if (Thread.interrupted())
        throw new InterruptedException();
     // common线程池是不能够被关闭的,所以直接调用awaitQuiescence,然后返回false
    if (this == common) {
        awaitQuiescence(timeout, unit);
        return false;
    }
    long nanos = unit.toNanos(timeout);
    if (isTerminated()) // 已经终结了,那么直接返回true
        return true;
    // 不等待,直接返回false
    if (nanos <= 0L)
        return false;
    // 计算等待的截止时间
    long deadline = System.nanoTime() + nanos;
    // 阻塞在this FJP对象的监视器锁中,直到状态变为TERMINATED然后被唤醒
    synchronized (this) {
        for (;;) {
            if (isTerminated()) // 要么关闭
                return true;
            if (nanos <= 0L) // 要么超时
                return false;
            long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
            wait(millis > 0L ? millis : 1L); // 等待即可
            nanos = deadline - System.nanoTime();
        }
    }
}

// FJT的方法,由FJP的工作线程调用
volatile int status; // 当前FJT的运行状态
static final int DONE_MASK   = 0xf0000000;  // 取完成位的掩码
static final int NORMAL      = 0xf0000000;  // 正常状态
static final int CANCELLED   = 0xc0000000;  // 被取消了
static final int EXCEPTIONAL = 0x80000000;  // 发生了异常
static final int SIGNAL      = 0x00010000;  // 需要唤醒
static final int SMASK       = 0x0000ffff;  // 取低16位的掩码
final int doExec() {
    int s; boolean completed;
    // 默认状态就是0,新建状态
    if ((s = status) >= 0) {
        try {
            completed = exec(); // 子类需要实现该方法完成调用
        } catch (Throwable rex) {
            return setExceptionalCompletion(rex);
        }
        // 如果执行完成,那么设置状态为NORMAL,表示正常完成
        if (completed)
            s = setCompletion(NORMAL);
    }
    return s;
}

// FJT的fork方法,将任务放入FJP中执行
public final ForkJoinTask<V> fork() {
    Thread t;
    if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
        // 如果是内部工作线程,那么直接将其放入自己的队列中
        ((ForkJoinWorkerThread)t).workQueue.push(this);
    else
        // 外部线程就调用externalPush,其实就是调用Common线程池来执行
        ForkJoinPool.common.externalPush(this);
    return this;
}

// FJP用于内部工作线程往自己的队列存放任务
final void push(ForkJoinTask<?> task) {
    ForkJoinTask<?>[] a; ForkJoinPool p;
    int b = base, s = top, n;
    if ((a = array) != null) {    // ignore if queue removed
        int m = a.length - 1;     // fenced write for task visibility
        // 获取top变量的偏移量,然后放入task对象
        U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task);
        // 对top值加1
        U.putOrderedInt(this, QTOP, s + 1);
        // 使用putOrdered保证了STORESTORE语义
        if ((n = s - b) <= 1) { // n为top引用和base引用中间的有效任务数
            // 为何这里要小于等于1才唤醒等待线程,只要这里大于1了,那么必然已经都唤醒了
            if ((p = pool) != null)
                p.signalWork(p.workQueues, this);
        }
        else if (n >= m)  // 满了扩容
            growArray();
    }
}

// 等待当前任务执行完完成
public final V join() {
    int s;
    // 等待过程中发现任务执行出了异常,然后调用reportException抛出异常
    if ((s = doJoin() & DONE_MASK) != NORMAL)
        reportException(s);
    // 正常完成就拿结果即可
    return getRawResult();
}

// CAS上把锁
private int lockRunState() {
    int rs;
    return ((((rs = runState) & RSLOCK) != 0 ||
             !U.compareAndSwapInt(this, RUNSTATE, rs, rs |= RSLOCK)) ?
            awaitRunStateLock() : rs);
}
// 等待获取锁
private int awaitRunStateLock() {
    Object lock;
    boolean wasInterrupted = false;
    for (int spins = SPINS, r = 0, rs, ns;;) {
        // 锁已经被释放,那么可以去CAS竞争锁
        if (((rs = runState) & RSLOCK) == 0) {
            if (U.compareAndSwapInt(this, RUNSTATE, rs, ns = rs | RSLOCK)) {
                if (wasInterrupted) {
                    try {
                        Thread.currentThread().interrupt();
                    } catch (SecurityException ignore) {
                    }
                }
                return ns;
            }
        }
        else if (r == 0) // 初始化随机数
            r = ThreadLocalRandom.nextSecondarySeed();
        else if (spins > 0) { // 随机,减少自旋次数
            r ^= r << 6; r ^= r >>> 21; r ^= r << 7; // 异或随机数
            if (r >= 0)
                --spins;
        }
        else if ((rs & STARTED) == 0 || (lock = stealCounter) == null)
            Thread.yield();   // 由于当前rs的STARTED状态位为0,代表了,当前FJP没有在运行了,那么没有必要再去睡眠了,因为这个状态维持时间会非常短
        // 光是睡眠不行,需要有人唤醒,所以这里必须置位 RSIGNAL 唤醒位,提示另外的线程需要唤醒它
        else if (U.compareAndSwapInt(this, RUNSTATE, rs, rs | RSIGNAL)) {
            synchronized (lock) {
                if ((runState & RSIGNAL) != 0) {
                    try {
                        lock.wait();
                    } catch (InterruptedException ie) {
                        if (!(Thread.currentThread() instanceof
                              ForkJoinWorkerThread))
                            wasInterrupted = true;
                    }
                }
                else
                    lock.notifyAll();
            }
        }
    }
}
// 解锁
private void unlockRunState(int oldRunState, int newRunState) {
    if (!U.compareAndSwapInt(this, RUNSTATE, oldRunState, newRunState)) {
        Object lock = stealCounter;
        runState = newRunState;              // clears RSIGNAL bit
        if (lock != null)
            synchronized (lock) { lock.notifyAll(); }
    }
}

著作权归NoLongerConfused所有。商业转载请联系NoLongerConfused获得授权,非商业转载请注明出处。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
是的,ForkJoinPoolJava中的一个线程池,主要用于执行分治任务。它是Java 7引入的一个新特性,可以利用多核处理器提高并行计算性能。ForkJoinPool使用工作窃取算法,即当一个线程的任务执行完后,它会从其他线程的任务队列中窃取任务来执行,以保证各个线程的任务负载较为均衡。 ForkJoinPool的使用方法与其他线程池类似,可以通过构造函数或者静态工厂方法来创建线程池。例如: ``` ForkJoinPool pool = new ForkJoinPool(); ``` 这样就创建了一个默认的ForkJoinPool线程池,它的线程数等于CPU核心数。也可以通过构造函数来指定线程池的参数,例如: ``` ForkJoinPool pool = new ForkJoinPool(4); ``` 这样就创建了一个包含4个线程ForkJoinPool线程池。在使用ForkJoinPool时,需要定义一个ForkJoinTask任务,例如: ``` class MyTask extends RecursiveTask<Integer> { protected Integer compute() { // 执行任务 } } // 创建任务 MyTask task = new MyTask(); // 执行任务 int result = pool.invoke(task); ``` 这里的MyTask是一个继承自ForkJoinTask的任务,它的compute()方法中定义了任务的具体执行过程。执行任务的方式是通过ForkJoinPool的invoke()方法来调用,它会返回任务的执行结果。 当然,除了invoke()方法之外,ForkJoinPool还提供了其他一些方法来执行任务,例如submit()和execute()方法。同时,ForkJoinPool也支持设置线程池的一些属性,例如任务窃取的策略、线程池的名称等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NoLongerConfused

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值