Java concurrent package source code learning thread pool (1) ThreadPoolExecutor source code analysis

The thread pool technology used in Java generally uses Executorsthis factory class, which provides a very simple method to create various types of thread pools:

public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor() 
public static ExecutorService newCachedThreadPool() 
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

The core interface in fact Executor, it is only a executemethod of abstraction to perform a task (Runnable Interface), the  ExecutorServiceinterface Executorprovides management of life-cycle tasks performed on the basis of, mainly submit, and shutdownmethods  AbstractExecutorServiceof ExecutorServicesome of the ways to do a default implementation , Mainly the submit and invoke methods, and the Executor interface executemethod of the real task execution is implemented by subclasses, that is ThreadPoolExecutor, it implements the task execution framework based on the thread pool, so to understand the thread pool of the JDK, then you have to look at this first class.

executeBefore looking at the method, we need to introduce a few variables or classes.

 

ctl

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

This variable is the core of the entire class, which AtomicIntegerensures that the operation of this variable is atomic. Through clever operations, ThreadPoolExecutor saves two contents with this variable:

  • The number of all active threads
  • The state of each thread (runState)

The lower 29 bits store the number of threads and the upper 3 bits store runState, so runState has 5 values:

  • RUNNING:-536870912
  • SHUTDOWN:0
  • STOP:536870912
  • TIDYING:1073741824
  • TERMINATED:1610612736

The transition between the various states in the thread pool is more complicated, and you can mainly remember the following:

  • RUNNING state: the thread pool is running normally, can accept new tasks and process tasks in the queue;
  • SHUTDOWN state: no longer accept new tasks, but will execute the tasks in the queue;
  • STOP state: no longer accept new tasks, do not process tasks in the queue

There are some operations around ctl variables. Understanding these methods is the basis for understanding some obscure code behind:

  View Code

 

corePoolSize

The size of the core thread pool. If the active thread is smaller than corePoolSize, it will be created directly.

 

keepAliveTime

The thread gets the timeout time of the task from the queue, that is, if the thread is idle for more than this time, it will be terminated.

 

Worker

private final class Worker extends AbstractQueuedSynchronizer implements Runnable ...

内部类Worker是对任务的封装,所有submit的Runnable都被封装成了Worker,它本身也是一个Runnable, 然后利用AQS框架(关于AQS可以看我这篇文章)实现了一个简单的非重入的互斥锁, 实现互斥锁主要目的是为了中断的时候判断线程是在空闲还是运行,可以看后面shutdownshutdownNow方法的分析。

复制代码
// state只有0和1,互斥
protected boolean tryAcquire(int unused) {
    if (compareAndSetState(0, 1)) {
        setExclusiveOwnerThread(Thread.currentThread());
        return true;// 成功获得锁
    }
    // 线程进入等待队列
    return false;
}

protected boolean tryRelease(int unused) {
    setExclusiveOwnerThread(null);
    setState(0);
    return true;
}
复制代码

之所以不用ReentrantLock是为了避免任务执行的代码中修改线程池的变量,如setCorePoolSize,因为ReentrantLock是可重入的。

 

execute

execute方法主要三个步骤:

  • 活动线程小于corePoolSize的时候创建新的线程;
  • 活动线程大于corePoolSize时都是先加入到任务队列当中;
  • 任务队列满了再去启动新的线程,如果线程数达到最大值就拒绝任务。
复制代码
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    int c = ctl.get();
    // 活动线程数 < corePoolSize
    if (workerCountOf(c) < corePoolSize) {
        // 直接启动新的线程。第二个参数true:addWorker中会重新检查workerCount是否小于corePoolSize
        if (addWorker(command, true))
            // 添加成功返回
            return;
        c = ctl.get();
    }
    // 活动线程数 >= corePoolSize
    // runState为RUNNING && 队列未满
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // double check
        // 非RUNNING状态 则从workQueue中移除任务并拒绝
        if (!isRunning(recheck) && remove(command))
            reject(command);// 采用线程池指定的策略拒绝任务
        // 线程池处于RUNNING状态 || 线程池处于非RUNNING状态但是任务移除失败
        else if (workerCountOf(recheck) == 0)
            // 这行代码是为了SHUTDOWN状态下没有活动线程了,但是队列里还有任务没执行这种特殊情况。
            // 添加一个null任务是因为SHUTDOWN状态下,线程池不再接受新任务
            addWorker(null, false);

        // 两种情况:
        // 1.非RUNNING状态拒绝新的任务
        // 2.队列满了启动新的线程失败(workCount > maximumPoolSize)
    } else if (!addWorker(command, false))
        reject(command);
}
复制代码

注释比较清楚了就不再解释了,其中比较难理解的应该是addWorker(null, false);这一行,这要结合addWorker一起来看。 主要目的是防止HUTDOWN状态下没有活动线程了,但是队列里还有任务没执行这种特殊情况。

 

addWorker

这个方法理解起来比较费劲。

  View Code

 

runWorker

任务添加成功后实际执行的是runWorker这个方法,这个方法非常重要,简单来说它做的就是:

  • 第一次启动会执行初始化传进来的任务firstTask;
  • 然后会从workQueue中取任务执行,如果队列为空则等待keepAliveTime这么长时间。
  View Code

 

getTask

  View Code

 

processWorkerExit

线程退出会执行这个方法做一些清理工作。

  View Code

 

tryTerminate

processWorkerExit方法中会尝试调用tryTerminate来终止线程池。这个方法在任何可能导致线程池终止的动作后执行:比如减少wokerCount或SHUTDOWN状态下从队列中移除任务。

  View Code

 

shutdown和shutdownNow

shutdown这个方法会将runState置为SHUTDOWN,会终止所有空闲的线程。

复制代码
public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 线程池状态设为SHUTDOWN,如果已经至少是这个状态那么则直接返回
            advanceRunState(SHUTDOWN);
            // 注意这里是中断所有空闲的线程:runWorker中等待的线程被中断 → 进入processWorkerExit →
            // tryTerminate方法中会保证队列中剩余的任务得到执行。
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }
复制代码

shutdownNow方法将runState置为STOP。和shutdown方法的区别,这个方法会终止所有的线程。

复制代码
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        // STOP状态:不再接受新任务且不再执行队列中的任务。
        advanceRunState(STOP);
        // 中断所有线程
        interruptWorkers();
        // 返回队列中还没有被执行的任务。
        tasks = drainQueue();
    }
    finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}
复制代码

主要区别在于shutdown调用的是interruptIdleWorkers这个方法,而shutdownNow实际调用的是Worker类的interruptIfStarted方法:

复制代码
private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
            // w.tryLock能获取到锁,说明该线程没有在运行,因为runWorker中执行任务会先lock,
            // 因此保证了中断的肯定是空闲的线程。
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    }
    finally {
        mainLock.unlock();
    }
}
复制代码
复制代码
void interruptIfStarted() {
    Thread t;
    // 初始化时state == -1
    if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
        try {
            t.interrupt();
        } catch (SecurityException ignore) {
        }
    }
}
复制代码

这就是前面提到的Woker类实现AQS的主要作用。

注意:shutdown方法可能会在finalize被隐式的调用。

这篇博客基本都是代码跟注释,所以如果不是分析ThreadPoolExecutor源码的话看起来会非常无聊。

Guess you like

Origin blog.csdn.net/m0_37541228/article/details/77825474