线程池ThreadPoolExecutor介绍

一、创建线程池的必要性

       大多数并发应用程序都是围绕着任务执行来构造的,任务通常是抽象且离散的工作单元。在处理这些任务的时候,有几种方案可以采取。

  1. 串行执行:也就是说所有任务都交由同一个线程依次处理,弊端就是,如果执行某个任务发生长时间阻塞,则导致任务挤压过多,处理性能不足,如果是处理实时性要求比较高的任务,则可能导致任务长时间没有响应。
  2. 并发执行:并发执行任务也有两种方案。一种是每个任务都新建一个线程去处理,这样处理任务间互不相关,即使其中一个任务阻塞,也不会影响其他线程处理其他的任务。但是也有一个弊端就是当产生的任务不断增加,同时也需要新建线程的不断增加;由于线程的创建和销毁不是没有代价的,而且随着线程数量的不断增多,各个线程抢占CPU资源也会愈发激烈,比较明显的就是抢占CPU时间片资源,同时在线程切换时带来的上下文切换也给JVM带来了压力,这样任务过多采用这种方案也不可取。另外一种是创建线程池将这些任务交由线程池处理,这种方案是考虑到在现实应用中,处理的大多数任务的执行时间较短,所以可以让这些任务共享同一个线程。这样就减少了大量线程的创建和销毁带来的性能开销。通过使用java.util.concurrent.ThreadPoolExecutor提供的解决方案,可以让线程池中始终保持一定数目的工作线程,当有任务需要处理时,工作线程就开始执行,当任务较多时,这些任务可以缓存在阻塞队列中,带有空闲线程时,会交由空闲线程继续处理。

二、ThreadPoolExecutor构造器介绍

      接下来我们来看下ThreadPoolExecutor的几种初始化方法。

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

通过查看ThreadPoolExecutor的源代码我们可以知道,ThreadPoolExecutor虽然有四个构造器,但是其实前三个构造器都是调用第四个构造器初始化的。现在我们来了解下这些构造器参数的含义。

  • corePoolSize:源码注释是:corePoolSize the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set。通过注释我们可以知道,corePoolSize参数指定了线程池中保持工作的线程数量,如果线程池没有设置allowCoreThreadTimeOut属性的话,则线程池中会始终保持corePoolSize数量的线程,即使这其中存在空闲的线程。而且当线程池中的线程出现了异常,导致线程退出,线程数量小于corePoolSize时,ThreadPoolExecutor会新建工作线程以达到corePoolSize的数量。
  • maximumPoolSize:源码注释是:the maximum number of threads to allow in the pool。顾名思义就是线程池中可以同时运行线程的最大数量。要求是maximumPoolSize >= corePoolSize,当maximumPoolSize大于corePoolSize时,如果交由线程池处理的任务数量超过corePoolSize时,则线程池会再新建一个线程去处理提交的任务,直到线程池中线程的数量达到maximumPoolSize时,如果再提交任务给线程池,则线程池会把任务放在阻塞队列中。
  • keepAliveTime&unit:unit参数是keepAliveTime参数的时间单位。keepAliveTime的源码注释是:when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.这个参数的意思就是,当线程池中的线程数量超过corePoolSize时,如果线程池中的某个线程处于Idle(空闲)状态的时间超过了keepAliveTime的时间,则线程池会终止掉这个线程。在介绍corePoolSize时,我们提到了allowCoreThreadTimeOut这个参数,如果线程池设置了allowCoreThreadTimeOut=true,则keepAliveTime的生效不再受线程池中线程数量是否超过corePoolSize的条件了。
  • workQueue:源码注释:the queue to use for holding tasks before they are executed.  This queue will hold only the {@code Runnable} tasks submitted by the {@code execute} method.从注释上我们可以知道当交由线程池处理的任务,没有空闲线程去立即处理的时候,线程池会把任务放到阻塞队列中。
  • threadFactory:源码注释:the factory to use when the executor creates a new thread。当线程池新建工作线程时,会调用这个线程工厂去创建。如果没有显示的初始化这个线程工厂的话,线程池构造器会默认使用DefaultThreadFactory工厂来新建线程,
  • handler:源码注释:the handler to use when execution is blocked because the thread bounds and queue capacities are reached。从注释中可以看出,如果线程池所有线程都在工作且没有空闲线程(也就是线程数量达到了maximumPoolSize),同时缓存在阻塞队列中的任务数也到达了阻塞队列的容量时,这时如果再提交任务给线程池处理的话,线程池就会调用handler去处理这个任务。置于是如何处理的,是拒绝,还是在主线程中执行,这就要看handler是如何初始化的了。handle的处理策略分如下几种:

              1、AbortPolicy:源码注释:A handler for rejected tasks that throws a {@code RejectedExecutionException}。 这种策略就是直接抛出异常,让调用线程池execute方法的调用者感知到。(线程池默认采用这种处理策略)

public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}

              2、CallerRunsPolicy:源码注释:A handler for rejected tasks that runs the rejected task directly in the calling thread of the {@code execute} method, unless the executor has been shut down, in which case the task is discarded.这种策略就是,调用线程池exceute方法的线程会去执行这个任务,前提条件是这个线程池没有被shutdown,如果这个线程池被shutdown了,则这个任务会被忽略掉。采用这种策略的一个好处就是,待处理的任务不会丢,也就是交由线程池处理的任务最重都执行完毕,即使线程池当前没有空闲线程执行且任务队列中也没有空余位置缓存这个任务,只要采用这种策略,线程池的调用线程就会去执行这个任务,代价就是线程池调用线程会因此而阻塞,直到它执行完这个任务。

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

            3、DiscardPolicy:源码注释:A handler for rejected tasks that silently discards the rejected task。这种策略是采用静默的方式直接摒弃这个任务,同时上层调用者也不会感知到。

public static class DiscardPolicy implements RejectedExecutionHandler {
    public DiscardPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

          4、DiscardOldestPolicy:源码注释:A handler for rejected tasks that discards the oldest unhandled request and then retries {@code execute}, unless the executor is shut down, in which case the task is discarded。从注释中我们可以看出这种策略就是如果线程池没有被shutdown的话,则丢掉一个未处理的任务,然后重复尝试执行execute的方法。从源码中我们可以看到会从任务队列的首部移出一个任务,然后再调用execute方法。

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public DiscardOldestPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}

            



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值