为什么用线程池?解释下线程池参数?

基于:Java 线程池七个参数详解

参考:为什么使用线程池,参数解释、《Java 编程的逻辑》- 马俊昌

为什么用线程池

1、降低资源消耗:提高线程利用率,降低创建和销毁线程的消耗;

2、提高响应速度:任务来了,直接有线程可用可执行,而不用先创建线程,再执行;

3、提高线程的可管理性:线程是稀缺资源,使用线程池可以统一分配调优监控。

线程池参数

Java 并发包中线程池的实现类是 ThreadPoolExecutor,ThreadPoolExecutor 有一些重要的参数:

public class ThreadPoolExecutor extends AbstractExecutorService {
  public ThreadPoolExecutor(int corePoolSize,
                                int maximumPoolSize,
                                long keepAliveTime,
                                TimeUnit unit,
                                BlockingQueue<Runnable> workQueue,
                                ThreadFactory threadFactory,
                                RejectedExecutionHandler handler) {
      // ...
  }
}

1、int corePoolSize代表核心线程数。线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了 allowCoreThreadTimeOut。这里的最小线程数量即是 corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了 corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务。

2、int maximumPoolSize代表的是最大线程数。当前线程数达到 corePoolSize 后,如果继续有任务被提交到线程池,会将任务缓存到工作队列(后面会介绍)中。如果队列也已满,则会去创建一个新线程来出来这个处理。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由 maximumPoolSize 指定。

3、long keepAliveTime表示空闲存活时间。一个线程如果处于空闲状态,并且当前的线程数量大于 corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由 keepAliveTime 来设定。

4、TimeUnit unit表示 keepAliveTime 的时间单位。

5、BlockingQueue<Runnable> workQueue表示工作队列,用来存放待执行的任务。有以下几种常见的工作队列:

  1. ArrayBlockingQueue

    基于数组的有界阻塞队列,按 FIFO 排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到 corePoolSize 后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到 maxPoolSize,则会执行拒绝策略。

  2. LinkedBlockingQueue

    基于链表的“有界”阻塞队列(默认最大容量为 Integer.MAX),按照 FIFO 排序。由于该队列最大容量为 Integer.MAX 时近似无界性,当线程池中线程数量达到 corePoolSize 后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到 maxPoolSize(很难达到 Integer.MAX 这个数),因此使用该工作队列时,参数 maxPoolSize 其实是不起作用的。

  3. SynchronousQueue

    一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到 maxPoolSize,则执行拒绝策略。

  4. PriorityBlockingQueue

    具有优先级的无界阻塞队列,优先级通过参数 Comparator 实现。

6、ThreadFactory threadFactory线程工厂,用来生产线程执行任务。我们可以选择使用默认的创建工厂,产生的线程都在同一个组内,拥有相同的优先级,且都不是守护线程。当然我们也可以选择自定义线程工厂,用来设定线程名、是否为 daemon 线程等等。

7、RejectedExecutionHandler handler任务拒绝策略。当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,JDK 中提供了 4 中拒绝策略:

  1. CallerRunsPolicy

    该策略下,在调用者线程中直接执行被拒绝任务的 run 方法,除非线程池已经 shutdown,则直接抛弃任务。

    public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
        */
        public CallerRunsPolicy() { }
    
        /**
         * Executes task r in the caller's thread, unless the executor
        * has been shut down, in which case the task is discarded.
        *
        * @param r the runnable task requested to be executed
        * @param e the executor attempting to execute this task
        */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }
    
  2. AbortPolicy

    该策略下,直接丢弃任务,并抛出 RejectedExecutionException 异常。

    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
        */
        public AbortPolicy() { }
    
        /**
         * Always throws RejectedExecutionException.
        *
        * @param r the runnable task requested to be executed
        * @param e the executor attempting to execute this task
        * @throws RejectedExecutionException always
        */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                  " rejected from " +
                                                  e.toString());
        }
    }
    
  3. DiscardPolicy

    该策略下,直接丢弃任务,什么都不做。

    public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
        */
        public DiscardPolicy() { }
    
        /**
         * Does nothing, which has the effect of discarding task r.
        *
        * @param r the runnable task requested to be executed
        * @param e the executor attempting to execute this task
        */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }
    
  4. DiscardOldestPolicy

    该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列。

    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
        */
        public DiscardOldestPolicy() { }
    
        /**
         * Obtains and ignores the next task that the executor
        * would otherwise execute, if one is immediately available,
        * and then retries execution of task r, unless the executor
        * is shut down, in which case task r is instead discarded.
        *
        * @param r the runnable task requested to be executed
        * @param e the executor attempting to execute this task
        */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }
    
posted @ 2024-07-09 15:52  Higurashi-kagome  阅读(1)  评论(0编辑  收藏  举报