Thread pool ThreadPoolExecutor [top-down understanding]

top-down learning

First, learn to understand in your own words

1.1 Why do you need a thread pool?

Because the JVM will switch the kernel state to create threads, frequent creation of threads will occupy CPU resources and will not solve any business. We hope that more CPU resources are used for business processing, not for thread creation and destruction. So use the idea of ​​pooling to cache created threads.

1.2 In order to meet this demand, how is the thread pool designed?

First we need to describe some basic concepts:

1.2.1 How to describe threads -Worker

1.2.2 How to represent tasks—Runable

The essence of the thread pool is to cache threads and tasks, so there must be a thread collection workerSetto cache threads ( Workerobjects) and a blocking queue workQueueto cache tasks ( Runable). The threads in the workerSet will continuously obtain threads from the workQueue and execute them. When there is no task in the workQueue, the worker will block until there is a task in the queue, and it will be taken out to continue execution.

image.png

1.3 How to design the api under this design reasonably?

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
复制代码

corePoolSizeand the size of the maximumPoolSizecorresponding pool and
keepAliveTime the time of the corresponding cache thread, the specific implementation of the corresponding cache , the creation of the corresponding thread, and the rejection policy when the cache cannot be storedunit
workQueue
threadFactoryhandler

1.4 How to briefly describe how the thread pool works?

When a task is submitted to the thread pool:

  1. Whether the thread pool first currently has fewer threads running than corePoolSize. If it is, create a new worker thread to execute the task. If both are executing tasks, go to 2.

  2. Determine whether the BlockingQueue is full, if not, put the thread into the BlockingQueue. Otherwise go to 3.

  3. If creating a new worker thread will cause the number of currently running threads to exceed the maximumPoolSize, it will be handed over to the RejectedExecutionHandler to handle the task.

Second, the following is just a description of the code

2.0 Explain some member variables of the thread pool

//这个属性是用来存放 当前运行的worker数量以及线程池状态的
//int是32位的,这里把int的高3位拿来充当线程池状态的标志位,后29位拿来充当当前运行worker的数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//存放任务的阻塞队列
private final BlockingQueue<Runnable> workQueue;
//worker的集合,用set来存放,重写equals和hashCode方法
private final HashSet<Worker> workers = new HashSet<Worker>();
//历史达到的worker数最大值
private int largestPoolSize;
//当队列满了并且worker的数量达到maxSize的时候,执行具体的拒绝策略
private volatile RejectedExecutionHandler handler;
//超出coreSize的worker的生存时间
private volatile long keepAliveTime;
//常驻worker的数量
private volatile int corePoolSize;
//最大worker的数量,一般当workQueue满了才会用到这个参数
private volatile int maximumPoolSize;
复制代码

internal state

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
复制代码

其中AtomicInteger变量ctl的功能非常强大: 利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:

  • RUNNING: 即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
  • SHUTDOWN: ,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
  • STOP : 即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
  • TIDYING :,即高3位为010, 所有的任务都已经终止;
  • TERMINATED:即高3位为011, terminated()方法已经执行完成

image.png

2.1 为什么需要Worker来描述线程?Worker有什么作用?

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
复制代码

分析一个类的作用,首先看他实现了那些接口。Worker继承了Aqs和Runnable。

  • 继承了AQS类,可以方便的实现工作线程的中止操作;

  • 实现了Runnable接口,可以将自身作为一个任务在工作线程中执行;

  • 当前提交的任务firstTask作为参数传入Worker的构造方法;

Worker的run方法执行的是线程池的runWorker方法

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // 中断相关的代码,中断的时候统一处理
    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);
    }
}
复制代码

runWorker方法是线程池的核心:

  • 线程启动之后,通过unlock方法释放锁,设置AQS的state为0,表示运行可中断;

  • Worker执行firstTask或从workQueue中获取任务:

  • 进行加锁操作,保证thread不被其他线程中断(除非线程池被中断)

  • 检查线程池状态,倘若线程池处于中断状态,当前线程将中断。

  • 执行beforeExecute

  • 执行任务的run方法

  • 执行afterExecute方法

  • 解锁操作

2.2 Worker是怎样获取任务的?

通过getTask方法从阻塞队列中获取等待的任务,如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源;

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.
        // 线程池正在停止或者任务队列为空,则工作线程数wc减去1,然后直接返回null
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        
        // 跑到这里说明线程池还处于RUNNING状态,重新获取一次工作线程数
        int wc = workerCountOf(c);

        // 超时时间控制
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

// 1.wc > maximumPoolSize说明当前的工作线程总数大于maximumPoolSize,说明setMaximumPoolSize()方法减少了线程池容量\
// 或者 2.timed && timedOut说明了线程命中了超时控制并且上一轮循环通过poll()方法从任务队列中拉取任务为null\
          // 并且 3. 工作线程总数大于1或者任务队列为空,则通过CAS把线程数减去1,同时返回null,\
          // CAS把线程数减去1失败会进入下一轮循环做重试
        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;
        }
    }
}
复制代码

注意这里一段代码是keepAliveTime起作用的关键:

boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

Runnable r = timed ?

                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :

                workQueue.take();
复制代码

If allowCoreThreadTimeOut is false, the thread will not be destroyed even if it is idle; if it is true, it will be destroyed if it is still idle within keepAliveTime.

If the thread is allowed to wait idle without being destroyed timed == false, workQueue.take task: If the blocking queue is empty, the current thread will be suspended and waited; when a task is added to the queue, the thread is awakened, and the take method returns the task, and execute;

If the thread does not allow endless idle timed == true, workQueue.poll task: If there is still no task in the blocking queue within the keepAliveTime time, return null;

2.3 How is the task performed?

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {  
    //workerCountOf获取线程池的当前线程数;小于corePoolSize,执行addWorker创建新线程执行command任务
       if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // double check: c, recheck
    // 线程池处于RUNNING状态,把提交的任务成功放入阻塞队列中
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // recheck and if necessary 回滚到入队操作前,即倘若线程池shutdown状态,就remove(command)
        //如果线程池没有RUNNING,成功从阻塞队列中删除任务,执行reject方法处理任务
        if (! isRunning(recheck) && remove(command))
            reject(command);
        //线程池处于running状态,但是没有线程,则创建线程
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 往线程池中创建新的线程失败,则reject任务
    else if (!addWorker(command, false))
        reject(command);
}
复制代码

It can be seen from the implementation of the method execute: addWorker is mainly responsible for creating new threads and executing tasks. When the thread pool creates a new thread to execute tasks, it needs to acquire a global lock:

private boolean addWorker(Runnable firstTask, boolean core) {
    // CAS更新线程池数量
    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;
    Worker w = null;
    try {
        w = new 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.thread(firstTask).start());
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}
复制代码

Guess you like

Origin juejin.im/post/7080541042134482952
Recommended