It has been almost 3 weeks since the last technical article was updated, as explained in the last article. Without further ado, let’s start our second reading of the thread pool source code.

review

A quick review of the thread pool source code covers two methods: execute(), an entry to execute a task, and addWorker(), most commonly understood as whether a new thread needs to be added. At the end of addWoker(), there is this code

if (workerAdded) {
    t.start();
    workerStarted = true;
}
Copy the code

It is obvious that multithreading is enabled through the start() method. If you want to see the execution logic of the thread, you need to check the run method in the corresponding class. Here, T is a member variable in the Worker class, so the focus is on the run() method in the Worker class.

runWorker()

The source code for the run() method is shown here, and finally to runWorker().

Look directly at the source code for runWorker

  1. It starts as a loop that either executes the firstTask that comes with the worker (firstTask) or passesgetTask()Access to task
  2. A task must first ensure that the thread pool is healthy, which is invoked in both caseswt.interrupt()The thread sets the interrupt flag bit
    1. The thread pool is stopped, that is, it does not accept new tasks and does not execute tasks in the queue
    2. If the flag bit of the thread is already true, then the flag bit is clear, and the thread pool is in the STOP state. This may seem awkward.
  3. Normally it is calledbeforeExecute()afterExecute()The parcel,task.run()

See how to customize the pre – and post-execution logic

Since it is written on a different computer, the example may not be exactly the same as the one in the previous article, but it conveys the same meaning

public class ThreadPoolExamples {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new MyThreadPoolExecutor(5.5.0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

        MyThread myThread = newMyThread(); executor.execute(myThread); }}class MyThread extends Thread {
    @Override
    public void run(a) {
        System.out.println(Thread.currentThread().getName() + " is running"); }}class MyThreadPoolExecutor extends ThreadPoolExecutor {
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        System.out.println("【" + Thread.currentThread().getName() + "Custom before execute");
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        System.out.println("【" + Thread.currentThread().getName() + "Is done");
    }

    MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); }}Copy the code

The first thing is to create your ownMyThreadPoolExecutorClass, inheritance,ThreadPoolExecutorAnd then rewritebeforeExecute()afterExecute()Define your own logic, and look at the test results

As you can see, both pre and post are working according to the established logic. Interestingly, 22 minutes have passed (don’t ask me why it took so long, I went to get takeout) and the thread pool still hasn’t stopped.

Note that the first step of runWorker() is a loop, and then the task is obtained by either firstTask or getTask(). If the task is not obtained by either method, the thread pool should exit.

getTask()

The main purpose, as the name implies, is to get the tasks that need to be performed and read the source code directly

  1. Come in is also a loop, can focus firstWhen does it exit the loop, it must beAbnormal conditionWill quit
    1. When the thread pool state is not RUNNING or SHUTDOWN, or when the thread is SHUTDOWN but there are no tasks in the work queue
    2. When WC is greater than the maximum number of threads and the task force is empty, or when WC is greater than the number of core threads and timedOut is true and the core queue is empty, or if allowCoreThreadTimeOut is set and WC > 1 or the core queue is empty
  2. In addition to the abnormal cases, the next step is to get the task from the work queue, but timed is usedpoll()ortake()
  3. If you can retrieve the task, go straight back to the task; If there are no tasks, either timeout sets timedOut to true or an exception is thrown to reset timedOut to false.

As you can see, only the above abnormal cases exit the loop and the task returns NULL, causing the while loop in runWorker() to exit and the entire thread pool to close. Otherwise it’s always going to be an endless loop in getTask().

The reason why thread pool can save resources is that the consumption of the thread pool is only reflected in the creation of Worker class, putting other tasks to be completed in the work queue, getTask() getting the task, and finally executing the task (call task.run())).

Rejection policies

This is called at the beginning of execute()

Please see the GIF below for details

RejectedExecution (Runnable r, ThreadPoolExecutor Executor) in the RejectedExecutionHandler interface; Method, and by default there are four implementations

There is an open interface, which is definitely capable of customizing implementation classes

class MyRejectedExecutionHandler implements RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println("task is rejected"); }}Copy the code

The thread pool of the JDK will be summarized in the next article. The thread pool of the JDK will be BlockingQueue.

Creation is not easy, if it is helpful to you, welcome to like, collect and share!

Below is a personal public number, interested can pay attention to, perhaps is your treasure public number oh, basic 2,3 days 1 more technical article!!