Home  >  Article  >  Java  >  A brief introduction to thread pools in Java programming

A brief introduction to thread pools in Java programming

高洛峰
高洛峰Original
2017-02-07 15:06:151333browse

Starting from Java 5, Java provides its own thread pool. A thread pool is a container of threads that only executes a rated number of threads at a time. java.util.concurrent.ThreadPoolExecutor is such a thread pool. It is very flexible, but also complicated to use. This article will give an introduction to it.

The first is the constructor. Take the simplest constructor as an example:

public ThreadPoolExecutor(
      int corePoolSize,
      int maximumPoolSize,
      long keepAliveTime,
      TimeUnit unit,
      BlockingQueue<Runnable> workQueue)

looks quite complicated. Here is an introduction.
corePoolSize refers to the reserved thread pool size.
maximumPoolSize refers to the maximum size of the thread pool.
keepAliveTime refers to the timeout for the end of the idle thread.
unit is an enumeration representing the unit of keepAliveTime.
workQueue represents the queue that stores tasks.
We can understand the meaning of these parameters from the working process of the thread pool. The working process of the thread pool is as follows:

1. When the thread pool is first created, there is no thread in it. The task queue is passed in as a parameter. However, even if there are tasks in the queue, the thread pool will not execute them immediately.

2. When the execute() method is called to add a task, the thread pool will make the following judgment:
a. If the number of running threads is less than corePoolSize, then create a thread immediately to run the task;
b. If the number of running threads is greater than or equal to corePoolSize, then put this task into the queue.
c. If the queue is full at this time, and the number of running threads is less than maximumPoolSize, then you still need to create a thread to run the task;
d. If the queue is full, and the number of running threads is greater than or equal to maximumPoolSize , then the thread pool will throw an exception and tell the caller "I can no longer accept tasks."

3. When a thread completes a task, it will take a task from the queue for execution.

4. When a thread has nothing to do and exceeds a certain time (keepAliveTime), the thread pool will determine that if the number of currently running threads is greater than corePoolSize, then the thread will be stopped. So after all tasks of the thread pool are completed, it will eventually shrink to the size of corePoolSize.

This process shows that it does not necessarily mean that the task will be executed first if it is added first. Assume that the queue size is 10, corePoolSize is 3, and maximumPoolSize is 6. Then when 20 tasks are added, the order of execution is as follows: tasks 1, 2, and 3 are executed first, and then tasks 4~13 are put into the queue. At this time, the queue is full, tasks 14, 15, and 16 will be executed immediately, and tasks 17~20 will throw an exception. The final order is: 1, 2, 3, 14, 15, 16, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13. The following is an example of using a thread pool:

public static void main(String[] args) {
  BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
  ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.DAYS, queue);
   
  for (int i = 0; i < 20; i++) {
    executor.execute(new Runnable() {
   
      public void run() {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(String.format("thread %d finished", this.hashCode()));
      }
    });
  }
  executor.shutdown();
}

The description of this example is as follows:

1. BlockingQueue is just an interface, commonly used The implementation classes include LinkedBlockingQueue and ArrayBlockingQueue. The advantage of using LinkedBlockingQueue is that there is no size limit. In this case, because the queue will not be full, execute() will not throw an exception, and the number of threads running in the thread pool will never exceed corePoolSize, so the keepAliveTime parameter will be meaningless.

2. The shutdown() method will not block. After calling the shutdown() method, the main thread ends immediately, and the thread pool will continue to run until all tasks are executed. If the shutdown() method is not called, the thread pool will be kept alive so that new tasks can be added at any time.

Here we have only introduced a small part of this thread pool. ThreadPoolExecutor is highly scalable, but the prerequisite for extending it is to be familiar with its working method. Later articles will introduce how to extend the ThreadPoolExecutor class.

ava.util.concurrent.ThreadPoolExecutor class provides rich extensibility. You can customize its behavior by subclassing it. For example, I want to print a message after each task ends, but I cannot modify the task object, then I can write like this:

ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue) {
  @Override
  protected void afterExecute(Runnable r, Throwable t) {
    System.out.println("Task finished.");
  }
};

In addition to the afterExecute method, the ThreadPoolExecutor class also has beforeExecute() and terminated () method can be overridden to be executed before the task is executed and after the entire thread pool is stopped.


除了可以添加任务执行前后的动作之外, ThreadPoolExecutor 还允许你自定义当添加任务失败后的执行策略。你可以调用线程池的 setRejectedExecutionHandler() 方法,用自定义的 RejectedExecutionHandler 对象替换现有的策略。 ThreadPoolExecutor 提供 4 个现有的策略,分别是:
ThreadPoolExecutor.AbortPolicy:表示拒绝任务并抛出异常
ThreadPoolExecutor.DiscardPolicy:表示拒绝任务但不做任何动作
ThreadPoolExecutor.CallerRunsPolicy:表示拒绝任务,并在调用者的线程中直接执行该任务
ThreadPoolExecutor.DiscardOldestPolicy:表示先丢弃任务队列中的第一个任务,然后把这个任务加进队列。
这里是一个例子:
ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

除此之外,你也可以通过实现 RejectedExecutionHandler 接口来编写自己的策略。下面是一个例子:

ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS, queue,
    new RejectedExecutionHandler() {
      public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println(String.format("Task %d rejected.", r.hashCode()));
      }
    }
);

更多简单介绍Java编程中的线程池相关文章请关注PHP中文网!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn