How does the Java thread pool reuse threads?

Principle overview

In fact, the implementation principle of Java thread pool is very simple. To put it bluntly, it is a thread collection workerSet and A blocking queue workQueue.

When the user submits a task to the thread pool, the thread pool will first put the task into workQueue. The threads in workerSet will continuously obtain threads from workQueue and then execute them . When there are no tasks in workQueue, worker will. until there is a task in the queue, then take it out and continue execution, Block

Insert image description here

The picture above is a simplified diagram of the work of the thread pool. The actual process is much more complicated than this, but these should be able to fully cover the entire workflow of the thread pool.

The whole process can be divided into the following parts:

1. Submit a task

When submitting a new task to the thread pool, the thread pool has three processing situations, namely:Create a worker thread to execute the task, Add the task to the blocking queue, Reject the task.

The process of submitting a task can also be split into the following parts:

  • When the number of worker threads is less than the number of core threads, create a new core worker thread directly.
  • When the number of worker threads is not less than the number of core threads, you need to try to add tasks to the blocking queue.
  • If it can be added successfully, it means that the queue is not full yet, then the following secondary verification needs to be done to ensure that the added task can be successfully executed.
    • Verify the running status of the current thread pool. If it is notRUNNING, you need to remove the task from the blocking queue and then reject the Task
    • Verify the number of worker threads in the current thread pool. If it is0, you need to actively add an empty worker thread to execute just Tasks added to the blocking queue
  • If the join fails, it means that the queue is full, and then a new "temporary" worker thread needs to be created to perform the task.
    • If the creation is successful, execute the task directly
    • If the creation fails, it means that the number of worker threads is already equal to the maximum number of threads, and the task can only be rejected.

The whole process can be represented by the following picture:

Insert image description here

2. Create a worker thread

Creating a worker thread requires a series of judgments. It must be ensured that the current thread pool can create a new thread before it can be created.

First, when the status of the thread pool isSHUTDOWN orSTOP, new threads cannot be created.

In addition, when the thread factory fails to create a thread, it cannot create a new thread.

There is also a comparison between the current number of working threads and the number of core threads and the maximum number of threads. If the former is greater than the latter, creation is not allowed.

In addition, it will try to increase the number of worker threads throughCAS. If the increase is successful, a new one will be created. Worker thread, i.e. Worker object.

Then lock it for secondary verification to see if the worker thread can be created. Finally, if the creation is successful, the worker thread will be started.

3. Start the working thread

When the worker thread is created successfully, that is, the Worker object has been created. At this time, you need to start the worker thread and let the thread Start working, Worker object is associated with a Thread, so To start a worker thread, just use worker.thread.start() to start the thread.

After startup, the Worker object's runWorker implements Runnable interface, so essentially Worker is also a thread.

Threadstart will be called after it is startedRunnable The run method, in the run method, is calledrun a> method , let him execute it. runWorker method, that is, the current object is passed to the runWorker(this)

4. Obtain tasks and execute them

After therunWorker method is called, it is time to perform a specific task. First, you need to get an executable task, and The a> object has a task bound to it by default. If the task is not empty, it will be executed directly. Worker

After the execution is completed, the task will be obtained from the blocking queue for execution. The process of obtaining the task needs to consider the current number of working threads.

  • Ifthe number of worker threads is greater than the number of core threads, then you need to passpoll to obtain, because at this time it is necessary to recycle idle threads;
  • Ifthe number of worker threads is less than or equal to the number of core threads, then you can passtakeallowCoreThreadTimeOut if is not needed Recycle, core threads to obtain, so at this time all threads are

How does the thread pool reuse threads?

In Java, eachThread thread has a start method, when the program calls the start method to start the thread, the Java virtual machine calls the run< of this class. /span> can therefore be inherited Thread class, in the run method, continuously calls the run method of the Runnable object passed in, and the program will continuously execute the code in the run method. method, run object’s Runnable method is actually calledrun class's Thread method. In the

Java thread pool can decouple threads and tasks, threads belong to threads, and tasks belong to tasks, getting rid of the previous processIn this "cyclic task", constantly check whether there are any tasks waiting to be executed, does not call Thread.start() to create a new thread every time a task is executed, but allows each thread to To execute a "cyclic task", and The thread pool encapsulates Thread. The core principle lies inthe same thread can continuously extract new tasks from the BlockingQueue to execute When creating a thread, one thread must correspond to the limit of one task. In the thread pool, Threadrun method, and change The run method is called with the same status as an ordinary method, which is equivalent to calling the run() of each task Methods are concatenated, so the number of threads does not increase.

Insert image description here

In the execute() method, call the addWorker() method multiple times to pass in the task, and the addWorker method will be added and started aWorker.

Who is this Worker here? Worker can be understood as a packaging of Thread. Worker has an object inside Thread, which It is the thread that actually executes the task in the end, Worker that assumes the role of task executor, and every methods of the object () are ultimately executed by the object. In fact, the object itself is both an AQS component and an object. TaskRunnablerun()WorkerWorkerRunnable

Insert image description here

So a Worker corresponds to a thread in the thread pool, and addWorker means adding a thread. The logic implementation of thread reuse is mainly in the run method in the Worker class In the runWorker method executed in , the simplified runWorker method code is as follows shown.

Insert image description here

It can be seen that the logic to realize thread reuse is mainly ina while loop body.

  • By taking of Worker or from by method Tasks to be performed. firstTaskgetTaskworkQueue
  • Directly call the method of task to perform specific tasks (instead of creating a new thread). run

Here, we find the final implementation, by taking Worker's firstTask or getTask method from The new task is taken out of a> to execute the task. That is, as mentioned before, each Threads are always in a large loop, repeatedly acquiring tasks and then executing tasks, thereby realizing thread reuse. method of workQueue and directly calls the Runnablerun

So to summarize, the approximate overall execution structure diagram should be as follows:

Insert image description here

On the whole, this is a design idea of ​​separation of responsibilities. Worker is Worker and task is task. They are not necessary. entangled together.

reference:

Guess you like

Origin blog.csdn.net/lyabc123456/article/details/134830968