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
- It starts as a loop that either executes the firstTask that comes with the worker (firstTask) or passes
getTask()
Access to task - A task must first ensure that the thread pool is healthy, which is invoked in both cases
wt.interrupt()
给The thread sets the interrupt flag bit- The thread pool is stopped, that is, it does not accept new tasks and does not execute tasks in the queue
- 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.
- Normally it is called
beforeExecute()
和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 ownMyThreadPoolExecutor
Class, inheritance,ThreadPoolExecutor
And 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
- Come in is also a loop, can focus firstWhen does it exit the loop, it must beAbnormal conditionWill quit
- 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
- 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
- In addition to the abnormal cases, the next step is to get the task from the work queue, but timed is used
poll()
ortake()
。 - 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!!