Java多线程学习笔记5——ThreadPoolExecutor

阿里代码规范中规定,线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程。这样做的好处是减少在创建和销毁所花的时间和系统开销。不使用线程池可能造成创建大量同类线程而导致消耗内存或则“过度切换”的问题。并且规定线程池不允许使用Executors创建。那么创建线程的方式基本就依赖于ThreadPoolExecutor此类了。

先了解下ThreadPoolExecutor的构造函数:

//构造函数使用默认的  DefaultThreadFactory  以及默认的AbortPolicy
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,   long keepAliveTime,  TimeUnit unit, 
                                                                                  BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
 }
   //使用默认的拒绝策略
    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,  long keepAliveTime,  TimeUnit unit,
                              BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
    }
    //以上两个两个构造函数调用此函数
    public ThreadPoolExecutor(int corePoolSize,   int maximumPoolSize,   long keepAliveTime, TimeUnit unit,
                          BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
           this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler);
    }

   //以上三个函数调用此函数
    public ThreadPoolExecutor(int corePoolSize,  int maximumPoolSize, long keepAliveTime,  TimeUnit unit,
                              BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,  RejectedExecutionHandler handler) {
        if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize ||   keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ? null :  AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

以上Overloading最终调用最后一个函数,集中解析下最后一个构造函数的参数就可以了。

new  ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)

corepoolsize:核心线程数   线程池创建后激活的线程数量

maxinumpoolSize:最大线程数  线程池提交的线程数,如果队列已经满了,而且超出最大线程数,会使用拒绝策略

keepAliveTime和timeUnit配置使用表示超过核心线程数的线程最大的空闲存货时间和单位

blockingQueue:保存等待执行的任务的阻塞队列 参数可以如下

ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,按FIFO原则进行排序
LinkedBlockingQueue:一个基于链表结构的阻塞队列,吞吐量高于ArrayBlockingQueue。
SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量高于LinkedBlockingQueue。
PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

threadFactory:通过线程工厂为每个创建出来的线程设置更有意义的名字

RejectedExecutionHandler:当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略还处理新提交的任务。它可以有如下四个选项:
AbortPolicy:直接抛出异常,默认情况下采用这种策略
CallerRunsPolicy:只用调用者所在线程来运行任务
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
DiscardPolicy:不处理,丢弃掉

     有了以上定义好的数据,下面来看看内部是如何实现的 。整个思路总结起来就是 5 句话:

  1. 如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。
  2. 如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列
  3. 如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。
  4. 如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。
  5. 线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出。

 

我们看下常用Executors的实现:

 //固定大小的线程池,多余的线程放入linkedBlockingQueue中,然后从对列中取线程
 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
//synchronousQueue不存储元素,有元素立即分配线程   
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
//其实就是单位为1的fixedThreaPool    
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

看了这么多实例我们可知,以上线程池如果线程过多可能会造成大量堆积待处理线程或则创建大量线程的问题。灵活使用ThreadPoolExecutor可以更加明确线程运行规则,避免资源耗尽的风险。

欢迎留言交流,共同讨论

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章