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

review

A brief review of the two methods covered in the thread pool source code in the previous article is that execute() is the entry point to the task and addWorker() is, most commonly understood, whether a new thread needs to be added. And at the end of addWoker() there is a piece of code that does this

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

If you want to see the execution logic of the thread, you need to look at the run method in the corresponding class. The t here is a member variable in the Worker class, so you should focus on the run() method in the Worker class.

runWorker()

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

Look directly at the RunWorker source code

  1. It starts as a loop that either executes or passes the worker’s own firstTaskgetTask()Access to task
  2. The task first needs to ensure that the thread pool is healthy. In both cases, call wt.interrupt() to set the interrupt flag bit to the thread

    1. The thread pool is in a STOP state, which means it does not accept new tasks and does not execute tasks in the queue
    2. If the thread flag bit is true, then the thread pool state is STOP. If the thread flag bit is true, then the thread pool state is STOP. This may seem awkward.
  3. Normally, it’s a callbeforeExecute()afterExecute()The parcel,task.run()

How to customize pre – and post-execution logic

Because is to change the computer to write, so the example may and the previous article is not exactly the same, but the expression is 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 = new MyThread(); executor.execute(myThread); } } class MyThread extends Thread { @Override public void run() { 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) {println(" [" + Thread.currentThread().getName() + "is done "); } MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); }}

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

You can see that both the pre and post are running according to their logic. Interestingly, 22 minutes later (don’t ask me why it took so long to get some takeout) the thread pool still hasn’t stopped. Why is this happening?

Note that when we analyze the first step of runWorker(), it is a loop, and then we get the task through firstTask or getTask(). If we fail to get the task by either method, the thread pool should exit.

getTask()

The main purpose, as the name suggests, is to get the task that needs to be performed and look directly at the source code

  1. Once in, it is also an infinite loop. You can first focus on when it will exit the loop. It must be abnormal to exit the loop

    1. When the thread pool state is not RUNNING or SHUTDOWN, or when the thread is SHUTDOWN but there is no work in the work queue
    2. When WC is greater than the maximum number of threads and the task force is listed as 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. Apart from the abnormal case, the next step is to fetch the task from the work queue, but this is determined by the TIMEDpoll()ortake()
  3. If you can retrieve the task, go back to it. If there is no task, either timeout is set to true, or an exception is thrown to reset TimedOut to false.

As you can see, only the above abnormal case exits the loop and the task returns null, resulting in
runWorker()The while loop exits in, and finally the entire thread pool closes. Otherwise will always be in
getTask()This is an infinite loop.

Thread pool can save resources because it simply creates the Worker class, puts the other tasks to be completed in the work queue, then getTask() gets the task, and finally executes the task (calls Task.run ())

Rejection policies

This is called at the very beginning of execute()

See the GIF below for details

As you can see, RejectedExecution (Runnable R, ThreadPoolExecutor Executor) is invoked in the RejectedExecutionHandler interface at the end; Method, and then by default there are four ways to implement it

There is an open interface, and it is certainly possible to customize the implementation class

class MyRejectedExecutionHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("task is rejected"); }}

In the next article, we will summarize the thread pool in the JDK.

It is not easy to create, if you have help, welcome thumb up, collection and share!

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