[JUC source code] Thread pool: ThreadPoolExecutor (four) source code analysis of task execution process

Thread pool series:

1.execute()

Entrance, select the execution strategy, divided into the following three situations:

  • Case 1: Worker thread <number of cores, create a thread to perform tasks
  • Case 2: Worker thread >= number of cores and the task queue is not full, join the task queue (waiting for the core thread to execute)
    • The thread pool is abnormal, delete the current task
    • Limiting situation: the available threads are just recycled when enqueuing, and a new thread without tasks is created
  • Situation 3: The task queue is full
    • The queue is full && number of threads <maxSize: create a new thread to process tasks
    • The queue is full && number of threads >= maxSize: Use the RejectedExecutionHandler class to reject the request
public void execute(Runnable command) {
    
    
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get(); // 获取 ctl 
    // 情况一:工作的线程小于核心线程数,创建新的线程,成功返回,失败不抛异常
    if (workerCountOf(c) < corePoolSize) {
    
    
        if (addWorker(command, true))
            return;
        // 由于 addWorker -> runWorker -> getTask,所以线程池状态可能发生变化
        c = ctl.get();
    }
    // 情况二:工作的线程大于等于核心线程数且任务队列没满
    // 注:isRunning是校验线程池状态是否正常。另外,offer不阻塞而是返回t/f
    if (isRunning(c) && workQueue.offer(command)) {
    
    
        int recheck = ctl.get();
        // 如果线程池状态异常 尝试从队列中移除任务,可以移除的话就拒绝掉任务
        if (!isRunning(recheck) && remove(command))
            reject(command);
        // 发现可运行的线程数是 0,就初始化一个线程,这里是个极限情况,入队的时候,突然发现可用线程都被回收了
        else if (workerCountOf(recheck) == 0)
            // Runnable是空的,不会影响新增线程,但是线程在 start 的时候不会运行
            // Thread.run() 里面有判断
            addWorker(null, false);
    }
    // 情况三:队列满了,开启线程到 maxSize,如果失败直接拒绝(这段逻辑可以在addWorker方法中看到)
    else if (!addWorker(command, false))
        reject(command);
}

2.addWorker()

Create a worker and return whether the worker is successfully started. The general process is as follows:

  1. Thread pool status verification
    • If it fails, it returns false. There are two reasons:
      • Abnormal thread pool status: SHUTDOWN, STOP, TIDYING, TERMINALED
      • Overflow of the number of worker threads: the number of threads >= capacity or in the case of using coreThread, the number of threads >= coreSize or maxSize
    • Success: CAS increases workCount by one
  2. Create Worker
    1. Create two identification variables: workerAdded, workerStarted
    2. Construct a worker, a new thread will be created through the newThread method during construction
    3. Lock, add the newly created worker to the container (Set) that manages the worker. Locks ensure thread safety during concurrency
  3. Start the thread in the worker, the calling logic is: Thread#start() -> Worker#run() -> runWorker()
// firstTask 不为空可以直接执行,为空执行不了,Thread.run()方法有判断,Runnable为空不执行
// core 为 true 表示线程最大新增个数是 coresize,false 表示最大新增个数是 maxsize
private boolean addWorker(Runnable firstTask, boolean core) {
    
    
    
	// break retry 跳到retry处,且不再进入循环
	// continue retry 跳到retry处,且再次进入循环
    retry:
--------------------------------------------------------------------------------------------------------------    
    // 1.先是各种状态的校验
    for (;;) {
    
    
        int c = ctl.get();
        int rs = runStateOf(c); // 获取线程池状态
        
        // 1.1 校验线程池状态,rs>=0:SHUTDOWN,STOP,TIDYING,TERMINALED
        if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) 
            return false;

        for (;;) {
    
    
            int wc = workerCountOf(c); // 得到当前工作线程数,即worker数
            // 1.2 校验工作中的线程数大于等于容量,或者大于等于 coreSize or maxSize
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize)) // 如果core为true就判断是否大于coreSize,否则判断maxSize
                return false;
            // CAS修改 workerCount(+1)
            if (compareAndIncrementWorkerCount(c))
                // break 结束 retry 的 for 循环
                break retry;
            // 到这里可能是CAS失败了,重新获取 ctl
            c = ctl.get();  
            // 如果线程池状态被更改
            if (runStateOf(c) != rs)
                continue retry; // 跳转到retry位置,重新判断
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
--------------------------------------------------------------------------------------------------------------
	// 2.创建worker
	// 2.1 创建标识变量
    boolean workerStarted = false; // woker启动标识
    boolean workerAdded = false;  // woker成功加入worker容器标识
    Worker w = null;
    try {
    
    
    	// 2.2 构造worker。在worker的构造函数中会调用newThread方法创建一个Thread
    	// 注:由于Worker也实现了Runnable,所以在创建线程的时候是newThread(this)。这是一个巧妙的设计
        w = new Worker(firstTask);
        final Thread t = w.thread; // 获取worker中的线程
        // 2.3 将worker加入到worker容器(Set)
        if (t != null) {
    
    
            final ReentrantLock mainLock = this.mainLock; // 这个mainLock是一个成员变量,作用是控制对worker的操作
            // 加锁是因为,可能有多个线程同时要将worker放入worker容器
            mainLock.lock();
            try {
    
    
				// 获取到线程池状态rs
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN || // 如果线程池状态是 RUNNING
                    (rs == SHUTDOWN && firstTask == null)) {
    
     // 线程池是SHUTDOWN且要执行的任务为null
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // 将当前woker加入到 HashSet<Worker> workers 中
                    workers.add(w); 
                    int s = workers.size(); // 获取到 workers 的大小,即现在有几个worker
                    // 如果worker数已经大于了最大线程池容量
                    if (s > largestPoolSize) 
                        largestPoolSize = s; // 将largestPoolSize设置为worker现在的书香
                    workerAdded = true; // 添加标志设置为成功
                }
            } finally {
    
    
                mainLock.unlock(); // 解锁
            }
--------------------------------------------------------------------------------------------------------------            
            // 3.启动如果woker中的线程。前提是worker已经添加成功
            if (workerAdded) {
    
    
                // 启动刚创建线程:Thread#start -> Worker#run -> runWorker
                t.start();
                workerStarted = true; // 线程启动标志置为true
            }
        }
    } finally {
    
    
    	// 如果线程启动失败
        if (! workerStarted) 
            addWorkerFailed(w);
    }
    // 返回线程是否启动成功
    return workerStarted;
}

3.runWorker()

First get the task, and then let the worker perform the task. The general logic of the method is as follows:

  1. There are two ways to get the task
    • firstTask: Worker's initial task
    • getTask(): the task of the task queue
  2. Lock to prevent the thread from being thrown into the task during execution
  3. If the thread pool is in STOP, interrupt the current thread
  4. Execute before hook function
  5. Execute the task, that is, call task.run()
  6. Execute the after hook function
  7. Delete the current task and release the lock. while execute the next task

One more note here, the purpose of while is to maintain the current thread to continue to perform tasks, but if the thread fails to get the task (the getTask method will block and wait), it will exit the loop, that is, the thread will be recycled at the end of its life.

final void runWorker(Worker w) {
    
    
    Thread wt = Thread.currentThread(); // 获取当前线程
    Runnable task = w.firstTask;  // 尝试获取创建worker时的firstTask
    
    w.firstTask = null; // 从这可以看出,只要firstTask执行过一次,就会一直被置为null
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
    
    
    	// 1.获取任务:如果firstTask已经被执行过了,就从任务队列中获取
    	// 注:通过while维持了线程的存活,并不断获取任务取执行。若迟迟拿不到任务,就会退出while结束线程
        while (task != null || (task = getTask()) != null) {
    
    
            // 2.锁住 worker,防止worker在执行任务时被丢入另一个任务
            w.lock();
            // 3.判断线程池若处于 stop 中,但线程没有到达中断状态,帮助线程中断
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
    
    
                // 4.执行 before 钩子函数
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
    
    
                    // 5.同步执行任务
                    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 {
    
    
                    // 6.执行 after 钩子函数
                    // 如果这里抛出异常,会覆盖 catch 的异常,所以这里异常最好不要抛出来
                    afterExecute(task, thrown);
                }
            } finally {
    
    
                // 7.任务执行完成,删除任务,并计算解锁
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
    
    
        // 做一些抛出异常的善后工作
        processWorkerExit(w, completedAbruptly);
    }
}

4.getTask()

Obtain tasks from the blocking queue. If the task is not retrieved after blocking and waiting, it will return null, so that the current thread exits the while loop in the runWorker method and is recycled, because there is nothing left to do and resources are wasted. The specific recycling strategy is in the source code, and the general process of the method is as follows:

  1. The first judgment to recycle the current thread: thread pool SHUTDOWN, and the queue is empty
  2. The second judgement is to recycle the current thread if any of the following conditions is met:
    1. wc> maximumPoolSize && wc> 1: The number of existing workers exceeds the maximum capacity of the thread pool, and there is at least one thread in the thread pool after recycling
    2. wc> maximumPoolSize && workQueue.isEmpty(): The number of existing workers exceeds the maximum capacity of the thread pool, and the task queue is empty
    3. timed && timedOut && wc> 1: Allow core threads to be recycled or the number of existing threads exceeds the number of cores, and the current thread has timed out, and there is at least one thread in the thread pool after recycling
    4. timed && timedOut && workQueue.isEmpty(): Allow to recycle core threads or the number of existing threads exceeds the number of cores, and the current thread has timed out, and the task queue is empty
  3. Get the task (take or poll) from the task queue, return if you get it, and set the timeout (timedOut) to true if you don't get it.
    Note: Only when timed is true will poll be used and then wait for the KeepAliveTime, otherwise it will use take all the time Wait for it

PS: Here again, core threads and non-core threads are only conceptually different. Everyone in the code is the same, they are all ordinary Threads.

private Runnable getTask() {
    
    
	// 标识是否超时
	// 默认false,但如果下面自旋中 poll 在 keepAliveTime(线程存活时间) 没等到任务,就会将timedOut置为true
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
    
    
        int c = ctl.get();
        int rs = runStateOf(c); // 获取线程池状态
        
		// 1.第一次判断是否回当前收线程
        // 线程池关闭 && 队列为空,不需要在运行了,直接返回null
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
            decrementWorkerCount(); // workerCount--
            return null;
        }

        int wc = workerCountOf(c); // 获取worker个数
        // timed的作用是决定在阻塞队列中等任务时用 poll 还是 take
        // timed = 核心线程可以被灭亡(默认false) || 运行的线程数大于 coreSize 
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
	   
        // 2. 第二次判断是否回收当前线程。组合后分为4种情况
        if ((wc > maximumPoolSize || (timed && timedOut))  // woker大于线程池最大数量 || (timed && 当前线程已经超时)
            && (wc > 1 || workQueue.isEmpty())) {
    
     // woker大于1 || 任务队列为空
            // 通过CAS使workerCount--
            if (compareAndDecrementWorkerCount(c)) 
                return null;
            continue;
        }

        try {
    
    
        	// 3.从阻塞队列中获取任务。timed 决定了是使用 poll 还是 take
        	// keepAliveTime 是线程最大空闲时间,是构造线程池的入参
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // poll,超时了就返回
                workQueue.take(); // take,任务队列中没任务会阻塞等待
            // 如果在队列拿到了任务就返回
            if (r != null)
                return r;
            // 没拿到就将超时timedOut设置为true,表示此时队列没有数据
            timedOut = true;
        } catch (InterruptedException retry) {
    
    
            timedOut = false;
        }
    }
}

summary

Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_43935927/article/details/113965324