Java thread pool ThreadPoolExecutor (on) --- Theory

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, 
long keepAliveTime, TimeUnit unit, 
BlockingQueue workQueue, 
RejectedExecutionHandler handler) 

corePoolSize : the minimum number of thread pool maintenance threads
maximumPoolSize : the maximum number of thread pool maintenance threads
keepAliveTime : the idle time allowed by the thread pool maintenance thread
unit : the unit of free time allowed by the thread pool maintenance thread
workQueue : the buffer used by the thread pool Queue
handler : Thread pool's processing strategy for rejected tasks

A task is added to the thread pool through the execute(Runnable) method. The task is an object of type Runnable, and the execution method of the task is the run() method of the object of type Runnable.

When a task is to be added to the thread pool through the execute(Runnable) method:

  1. If the number in the thread pool is less than corePoolSize at this time, even if the threads in the thread pool are idle, a new thread must be created to handle the added task.
  2. If the number of thread pools is equal to corePoolSize at this time, but the buffer queue workQueue is not full, then the task is put into the buffer queue.
  3. If the number in the thread pool is greater than corePoolSize at this time, the buffer queue workQueue is full, and the number in the thread pool is less than the maximumPoolSize, a new thread is created to process the added tasks.
  4. If the number in the thread pool is greater than corePoolSize, the buffer queue workQueue is full, and the number in the thread pool is equal to the maximumPoolSize, then the task is processed by the strategy specified by the handler.

That is, the priority of processing tasks is:

Core thread corePoolSize, task queue workQueue, maximum thread maximumPoolSize, if all three are full, use the handler to handle the rejected task.


When the number of threads in the thread pool is greater than corePoolSize, if a thread's idle time exceeds keepAliveTime, the thread will be terminated. In this way, the thread pool can dynamically adjust the number of threads in the pool.


The optional parameters of unit are several static attributes in java.util.concurrent.TimeUnit:
NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS.

The workQueue I use is: java.util.concurrent.ArrayBlockingQueue

The handler has four options:

  • ThreadPoolExecutor.AbortPolicy()

Throw java.util.concurrent.RejectedExecutionException

  • ThreadPoolExecutor.CallerRunsPolicy()

Retry to add the current task, he will automatically call the execute() method repeatedly

  • ThreadPoolExecutor.DiscardOldestPolicy()

Abandon the old task

  • ThreadPoolExecutor.DiscardPolicy()

Abandon the current task

Usage example:

package demo;

import java.io.Serializable;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThreadPool2
{
    
    
    private static int produceTaskSleepTime = 2;
    private static int produceTaskMaxNumber = 10;

    public static void main(String[] args)
    {
    
    
        // 构造一个线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
                new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 1; i <= produceTaskMaxNumber; i++)
        {
    
    
            try
            {
    
    
                // 产生一个任务,并将其加入到线程池
                String task = "task@ " + i;
                System.out.println("put " + task);
                threadPool.execute(new ThreadPoolTask(task));

                // 便于观察,等待一段时间
                Thread.sleep(produceTaskSleepTime);
            }
            catch (Exception e)
            {
    
    
                e.printStackTrace();
            }
        }
    }
}

/**
 * 线程池执行的任务
 */
class ThreadPoolTask implements Runnable, Serializable
{
    
    
    private static final long serialVersionUID = 0;
    private static int consumeTaskSleepTime = 2000;
    // 保存任务所需要的数据
    private Object threadPoolTaskData;

    ThreadPoolTask(Object tasks)
    {
    
    
        this.threadPoolTaskData = tasks;
    }

    public void run()
    {
    
    
        // 处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句
        System.out.println(Thread.currentThread().getName());
        System.out.println("start .." + threadPoolTaskData);

        try
        {
    
    
            // //便于观察,等待一段时间
            Thread.sleep(consumeTaskSleepTime);
        }
        catch (Exception e)
        {
    
    
            e.printStackTrace();
        }
        threadPoolTaskData = null;
    }

    public Object getTask()
    {
    
    
        return this.threadPoolTaskData;
    }
}

Explanation:
1. In this program, a task is an object of type Runnable, that is, an object of type ThreadPoolTask.
2. Generally speaking, in addition to the processing method, the task also needs to process the data, and the processed data is passed to the task through the construction method.
3. In this program, the main() method is equivalent to a cruel leader. He sends out many tasks and throws them to a hardworking team called threadPool.
There are at least two members in this group. If they two are too busy, the task will be placed in the task list.
If there are too many tasks and the task list is too large (more than 3), hire new team members to help. However, based on cost considerations, too many team members cannot be hired, at most 4 team members can be hired.
If the four team members are busy and there is a new task, the team will not be able to handle it. The task will be processed through a strategy. Our processing method is to keep dispatching until the task is accepted (more Cruel! Haha).
Because the work of the team members is costly, if the work is idle and there are no new tasks until 3SECONDS, then some team members will be fired. However, for the normal operation of the team, even if the work is idle, the team members will No less than two.
4. By adjusting the size of produceTaskSleepTime and consumeTaskSleepTime to control the speed of dispatching tasks and processing tasks, you can observe the working conditions of the program at different rates by changing these two values.
5. By adjusting the data referred to in 4, plus adjusting the task discarding strategy, and replacing it with the other three strategies, you can see different processing methods under different strategies.
6. For other usage methods, please refer to the help of jdk, it is easy to understand and use.

Guess you like

Origin blog.csdn.net/nikyae/article/details/111133970