线程池之ForkJoinPool

ForkJoinPool线程池是jdk 1.7引入的新线程池实现,那么它与之前1.5引入的ThreadPoolExecutor有什么不同呢?带着问题学习源码咯。本文仅简单学习,不深入(怕翻车-_-!)

ForkJoinPool模型

从简单的无参构造器入手吧

public ForkJoinPool() {
    this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
         defaultForkJoinWorkerThreadFactory, null, false);
}
public ForkJoinPool(int parallelism,
                    ForkJoinWorkerThreadFactory factory,
                    UncaughtExceptionHandler handler,
                    boolean asyncMode) {
    this(checkParallelism(parallelism),
         checkFactory(factory),
         handler,
         asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
         "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;
    long np = (long)(-parallelism); // offset ctl counts
    this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}
  1. workerNamePrefix:线程池worker命名前缀,默认为【“ForkJoinPool-” + 线程池序号(递增) + “-worker-”】
  2. factory:线程池的线程工厂类,默认为DefaultForkJoinWorkerThreadFactory
  3. ueh:线程的UncaughtExceptionHandler处理句柄
  4. config:线程池配置,共两个配置项:线程池并发数+线程池模式。默认模式为:LIFO_QUEUE=0(非异步模式)。异步模式配置为:FIFO_QUEUE=2的16次方。共享模式:SHARED_QUEUE=2的31次方
  5. ctl:主线程池控制符

ForkJoinPool属性

  1. parallelism并发配置:并发数并不是任意配置的,是有上限的,上限为0x7fff - 1=32767-1=32766。默认为机器有效核心数:Runtime.getRuntime().availableProcessors()
private static int checkParallelism(int parallelism) {
    if (parallelism <= 0 || parallelism > MAX_CAP)
        throw new IllegalArgumentException();
    return parallelism;
}
static final int MAX_CAP      = 0x7fff;        // max #workers - 1
  1. workQueues任务队列数组
  2. runState线程池运行状态
RSLOCK     = 1;
RSIGNAL    = 1 << 1;
STARTED    = 1 << 2;
STOP       = 1 << 29;
TERMINATED = 1 << 30;
SHUTDOWN   = 1 << 31;

ForkJoinPool模型

  1. ForkJoinPool:线程池
  2. ForkJoinWorkerThread:线程池内部线程
  3. DefaultForkJoinWorkerThreadFactory:线程池内部线程工厂类
  4. WorkQueue:任务队列

WorkQueue属性

  1. base:poll拉取下一个数据所在slot的索引
  2. top:push推入下一个数据所在slot的索引
  3. array:任务所在数组

ForkJoinPool提交任务

提交任务会将Runnable、Callable接口的实现类task封装为ForkJoinTask提交至线程池,我们重点关注任务进入线程池后的动作externalPush

public <T> ForkJoinTask<T> submit(Callable<T> task) {
    ForkJoinTask<T> job = new ForkJoinTask.AdaptedCallable<T>(task);
    externalPush(job);
    return job;
}

externalPush将任务入队

final void externalPush(ForkJoinTask<?> task) {
    WorkQueue[] ws; WorkQueue q; int m;
    // 1. 获取当前线程探针
    int r = ThreadLocalRandom.getProbe();
    // 2. rs初始值为0
    int rs = runState;
    // 3.1 将任务压入队列
    if ((ws = workQueues) != null && (m = (ws.length - 1)) >= 0 &&
        (q = ws[m & r & SQMASK]) != null && r != 0 && rs > 0 &&
        U.compareAndSwapInt(q, QLOCK, 0, 1)) {
        // 3.1.1 线程池已经启动
        // 3.1.2 线程池任务队列已初始化
        // 3.1.3 随机获取一个任务队列,对该队列加锁
        // 3.1.4 将任务压入任务队列
        ForkJoinTask<?>[] a; int am, n, s;
        if ((a = q.array) != null &&
            (am = a.length - 1) > (n = (s = q.top) - q.base)) {
            int j = ((am & s) << ASHIFT) + ABASE;
            U.putOrderedObject(a, j, task);
            U.putOrderedInt(q, QTOP, s + 1);
            U.putIntVolatile(q, QLOCK, 0);
            // 3.1.5 如果拉取数据的slot槽索引+1已经大于等于压入数据的slot槽索引
            if (n <= 1)
                // 3.1.6 尝试唤醒worker或创建worker(有任务入队列可以干活了,
                // 如果人手不够会尝试创建更多的工人,前提是预算允许的话)
                signalWork(ws, q);
            return;
        }
        U.compareAndSwapInt(q, QLOCK, 1, 0);
    }
    // 3.2 直接提交任务
    externalSubmit(task);
}

直接提交任务(生产者)

private void externalSubmit(ForkJoinTask<?> task) {
    // 如果当前线程探针未初始化则初始化探针,并获取探针
    int r;                                    // initialize caller's probe
    if ((r = ThreadLocalRandom.getProbe()) == 0) {
        ThreadLocalRandom.localInit();
        r = ThreadLocalRandom.getProbe();
    }
    // 自旋
    for (;;) {
        WorkQueue[] ws; WorkQueue q; int rs, m, k;
        boolean move = false;
        ...
        // 如果线程池任务队列数组未初始化,则初始化任务队列数组
        else if ((rs & STARTED) == 0 ||     // initialize
                 ((ws = workQueues) == null || (m = ws.length - 1) < 0)) {
            int ns = 0;
            rs = lockRunState();
            try {
                if ((rs & STARTED) == 0) {
                    ...
                    workQueues = new WorkQueue[n];
                    ns = STARTED;
                }
            } finally {
                unlockRunState(rs, (rs & ~RSLOCK) | ns);
            }
        }
        // 如果选中的任务队列数组下标处的队列非空,将任务压入队列
        else if ((q = ws[k = r & m & SQMASK]) != null) {
            if (q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) {
                ForkJoinTask<?>[] a = q.array;
                int s = q.top;
                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;
                        U.putOrderedObject(a, j, task);
                        U.putOrderedInt(q, QTOP, s + 1);
                        submitted = true;
                    }
                } finally {
                    U.compareAndSwapInt(q, QLOCK, 1, 0);
                }
                // 尝试唤醒工作者(有任务入队列可以干活了)
                if (submitted) {
                    signalWork(ws, q);
                    return;
                }
            }
            move = true;                   // move on failure
        }
        // 初始化任务队列(注意队列未绑定工作者即等待处理)并放入任务队列数组
        else if (((rs = runState) & RSLOCK) == 0) { // create new queue
            q = new WorkQueue(this, null);
            q.hint = r;
            q.config = k | SHARED_QUEUE;
            q.scanState = INACTIVE;
            rs = lockRunState();           // publish index
            if (rs > 0 &&  (ws = workQueues) != null &&
                k < ws.length && ws[k] == null)
                ws[k] = q;                 // else terminated
            unlockRunState(rs, rs & ~RSLOCK);
        }
        else
            // 当前线程太忙了,换个工人
            move = true;                   // move if busy
        if (move)
            // 当前队列已满移动探针,寻找其他空闲队列
            r = ThreadLocalRandom.advanceProbe(r);
    }
}

尝试唤醒或创建工作者(消费者)

final void signalWork(WorkQueue[] ws, WorkQueue q) {
    long c; int sp, i; WorkQueue v; Thread p;
    // 如果当前活跃工作者太少则尝试创建
    while ((c = ctl) < 0L) {                       // too few active
        // 当前活跃工作者不存在空闲的工作者
        if ((sp = (int)c) == 0) {                  // no idle workers
            // 工作者过少
            if ((c & ADD_WORKER) != 0L)            // too few workers
                // 尝试创建工作者
                tryAddWorker(c);
            break;
        }
        ...
        if ((v = ws[i]) == null)                   // terminating
            break;
        int vs = (sp + SS_SEQ) & ~INACTIVE;        // next scanState
        int d = sp - v.scanState;                  // screen CAS
        long nc = (UC_MASK & (c + AC_UNIT)) | (SP_MASK & v.stackPred);
        if (d == 0 && U.compareAndSwapLong(this, CTL, c, nc)) {
            // 任务队列当前处于活跃状态(有任务),如果存在工作者则唤醒工作者干活
            v.scanState = vs;                      // activate v
            if ((p = v.parker) != null)
                U.unpark(p);
            break;
        }
        // 没有更多的待处理任务,退出
        if (q != null && q.base == q.top)          // no more work
            break;
    }
}

创建工作者

private void tryAddWorker(long c) {
        		...
                createWorker();
                ...
}
private boolean createWorker() {
    ForkJoinWorkerThreadFactory fac = factory;
    ...
        if (fac != null && (wt = fac.newThread(this)) != null) {
            wt.start();
            return true;
    ...
}

线程工厂构建线程(工作者)

static final class DefaultForkJoinWorkerThreadFactory
    implements ForkJoinWorkerThreadFactory {
    public final ForkJoinWorkerThread newThread(ForkJoinPool pool) {
        return new ForkJoinWorkerThread(pool);
    }
}
protected ForkJoinWorkerThread(ForkJoinPool pool) {
    ...
    // 注册当前worker
    this.workQueue = pool.registerWorker(this);
}

线程池注册worker

final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
    UncaughtExceptionHandler handler;
    wt.setDaemon(true);                           // configure thread
    if ((handler = ueh) != null)
        wt.setUncaughtExceptionHandler(handler);
    // 初始化时队列中没有待处理任务
    WorkQueue w = new WorkQueue(this, wt);
    int i = 0;                                    // assign a pool index
    int mode = config & MODE_MASK;
    int rs = lockRunState();
    try {
        WorkQueue[] ws; int n;                    // skip if no array
        if ((ws = workQueues) != null && (n = ws.length) > 0) {
            ...
            w.hint = s;                           // use as random seed
            // 线程池中的index与模式mode
            w.config = i | mode;
            w.scanState = i;                      // publication fence
            ws[i] = w;
        }
    } ...
    wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
    return w;
}

执行工作者线程,工作者线程遍历任务队列数组,选择存在“待处理任务”的队列,遍历队列拉取“待处理任务”并执行

// 入参r被限制不能为0,依然能选择到workQueues中下标为0的任务队列,
// 因为r为随机数对队列(数组长度-1)进行取余,随机数可以整除(数组长度-1)时下标为0
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;                     // initially non-negative
        for (int origin = r & m, k = origin, oldSum = 0, checkSum = 0;;) {
            WorkQueue q; ForkJoinTask<?>[] a; ForkJoinTask<?> t;
            int b, n; long c;
            if ((q = ws[k]) != null) {
                if ((n = (b = q.base) - q.top) < 0 &&
                    (a = q.array) != null) {      // non-empty
                    long i = (((a.length - 1) & b) << ASHIFT) + ABASE;
                    if ((t = ((ForkJoinTask<?>)
                              U.getObjectVolatile(a, i))) != null &&
                        q.base == b) {
                        if (ss >= 0) {
                            if (U.compareAndSwapObject(a, i, t, null)) {
                                q.base = b + 1;
                                if (n < -1)       // signal others
                                    signalWork(ws, q);
                                return t;
                            }
                        }
            ...
            // 移动后再次扫描
            r ^= r << 1; r ^= r >>> 3; r ^= r << 10;
            origin = k = r & m;           // move and rescan
}

总结

  1. ForkJoinPool相对于传统的线程池更加轻量,可以理解其为“协程模式”实现高并发,此模式相对于传统模式需要更多的cpu资源
  2. 如下图,只要存在“待处理任务”,每个线程都会不间断的拉取任务处理数据。当然代码的复杂度也比传统线程池高出很多。算法更加复杂

线程池之ForkJoinPool_第1张图片

你可能感兴趣的:(java,线程池,并发编程,协程,高并发,队列)