ThreadPoolExecutor工作原理源码分析

ThreadPoolExecutor详解

ThreadPoolExecutor是JDK并发包提供的一个线程池服务,基于ThreadPoolExecutor可以很容易将一个Runnable接口的任务放入线程池中。

ThreadPoolExecutor的构建参数:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}
参数解释

corePoolSize:核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量;

maximumPoolSize: 线程池维护线程的最大数量 ;

keepAliveTime:线程池维护线程所允许的空闲时间,当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0;

unit:线程池维护线程所允许的空闲时间的单位、可选参数值为:TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS;

workQueue:线程池所使用的缓冲队列,常用的是:java.util.concurrent.ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue,性能方面:线程少 (<20) ,Queue长度短 (<30) , 使用SynchronousQueue ;线程多(>20),Queue长度长(>30),使用LinkedBlockingQueue;如果将来需要进行扩展还是选择LinkedBlockingQueue好,尽量把SynchronousQueue限制在特殊场景中使用;

handler:线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,默认值ThreadPoolExecutor.AbortPolicy();

ThreadPoolExecutor任务拒绝策略:

ThreadPoolExecutor内部有实现4个拒绝策略RejectedExecutionHandler:

(1)CallerRunsPolicy,由调用execute方法提交任务的线程来执行这个任务,如果线程池关闭了,这个任务将会被丢弃;

(2)AbortPolicy,抛出异常RejectedExecutionException拒绝提交任务;

(3)DiscardPolicy,直接抛弃任务,不做任何处理,源码中是空的,无任何处理代码;

(4)DiscardOldestPolicy,去除任务队列中的第一个任务,重新提交到队列尾部;

execute方法源码
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)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

ThreadPoolExecutor中的ctl变量:

ctl是一个AtomicInteger类型的变量,它包含两个概念:

1.workerCount:表明当前有效的线程数,wc

2.runState:表明当前线程池的状态,rs,是否处于Running,Shutdown,Stop,Tidying,Terminate五种状态。

为了把这两种状态放到一个int值中保存,代码中限定了workerCount的值是2^29-1,因为还有五种状态需要表示,需要3位才能表示五种状态,所以会有29位来表示workerCount,而剩下的3位来表示当前线程池的状态。所以说COUNT_BITS为Integer.SIZE(32位)- 3,即29位。CAPACITY = (1 << COUNT_BITS) - 1,为28个1;

1.如果当前线程数小于corePoolSize,调用addWorker方法增加核心线程数(core为true是增加核心线程数,为false是增加非核心线程数),成功则返回,若失败则说明不符合创建核心线程数条件(如线程状态不对等);

2.如果当先线程数大于等于corePoolSize,将command插入缓冲队列,成功则执行完毕;

3.如果2中if判断失败,则说明缓存队列已满,进入if else中,增加非核心线程数,如果目前线程数大于等于maximumPoolSize,则抛出异常;

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                        firstTask == null &&
                        ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    ThreadPoolExecutor.Worker w = null;
    try {
        w = new ThreadPoolExecutor.Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start();//在被构造为Worker工作线程,且被加入到工作线程集合中后,执行线程任务,注意这里的start实际上执行Worker中run方法,其中实际调用了runWorker方法
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

1.获取当前线程池的状态,如果是STOP,TIDYING,TERMINATED状态的话,则会返回false,如果现在状态是SHUTDOWN,但是firstTask不为空或者workQueue为空的话,那么直接返回false。

2.通过自旋的方式,判断要添加的Worker是否是corePool,如果是的话,那么则判断当前的workerCount是否大于corePoolsize,否则则判断是否大于maximumPoolSize,如果满足的话,说明workerCount超出了线程池大小,直接返回false。如果小于的话,那么判断是否成功将WorkerCount通过CAS操作增加1,如果增加成功的话。则进行到第3步,否则则判断当前线程池的状态,如果现在获取到的状态与进入自旋的状态不一致的话,那么则通过continue retry重新进行状态的判断。

3.如果满足了的话,那么则创建一个新的Worker对象,然后获取线程池的重入锁后,判断当前线程池的状态,如果当前线程池状态为STOP,TIDYING,TERMINATED的话,那么调用decrementWorkerCount将workerCount减一,然后调用tryTerminate停止线程池,并且返回false。

4.如果状态满足的话,那么则在workers中将新创建的worker添加,并且重新计算largestPoolSize,然后启动Worker中的线程开始执行任务。

5.重新Check一次当前线程池的状态,如果处于STOP状态的话,那么就调用interrupt方法中断线程执行。

final void runWorker(ThreadPoolExecutor.Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                    (Thread.interrupted() &&
                            runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

这里会先执行beforeExecute,最后执行afterExecute;在runWorker中不断循环getTask去workQueue中获取线程对象,直到都循环完毕;

线程是如何重复利用的

runWoker取任务有两个方式:

  1. firstTask:这是指定的第一个runnable可执行任务,它会在Woker这个工作线程中运行执行任务run。并且置空表示这个任务已经被执行。 这也是为什么task的run方法只能执行一次的原因
  2. getTask():这首先是一个死循环过程,工作线程循环直到能够取出Runnable对象或超时返回,这里的取的目标就是任务队列workQueue,对应刚才入队的操作,有入有出。
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            // 最关键的一句
            Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

线程池的线程复用:就是任务在并不只执行创建时指定的firstTask第一任务,还会从任务队列的中自己主动取任务执行,而且是有/无时间限定的阻塞等待,保证线程的存活。

getTask的两种取任务的方式:

  1. 无限定时间:默认不允许core核心线程超时allowCoreThreadTimeOut=false(允许的话就是所有的线程都有超时机制),以及判定当前是core核心线程,没有限定时间的方式,就会一直在take()中阻塞直到取出任务。这就是核心线程默认的情况,一直阻塞一直存活一直工作。
  2. 有限定时间:那就是开启allowCoreThreadTimeOut的核心线程,以及work工作线程,这是keepAliveTime有效作用的唯一地方,它决定了任务线程从任务队列取任务(出队)时poll(),最大的等待时间,在这段时间keepAliveTime就是线程的有限的存活时间。如果在限定时间之后取不出任务,就出现超时,对外返回null,最终让当前线程结束。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一只努力的微服务

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

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

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

打赏作者

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

抵扣说明:

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

余额充值