并发包的线程池第二篇--Executors的构造

上一篇讲述了ThreadPoolExecutor的执行过程,我们也能看出来一个很明显的问题:这个线程池的构造函数比较复杂,对于不十分理解其运作原理的程序员,自己构造它可能体现和想象中不一样的行为。比如阻塞队列放什么,corePoolSize怎么设置等等。

所以和Math这种工具类一样,并发包也提供了一种工具类:Executors。

首先这个工具类的作用就是:提供静态方法帮你构造不同的线程池。那么先分析一下它的设计模式:

1,静态工厂方法模式,静态方法帮你构造线程池。

2,外观模式,用一个简单的接口屏蔽了内部细节。(这么说有一点点牵强)

这个Executors提供的最主要的工厂有三种:

 1     public static ExecutorService newFixedThreadPool(int nThreads) {
 2         return new ThreadPoolExecutor(nThreads, nThreads,
 3                                       0L, TimeUnit.MILLISECONDS,
 4                                       new LinkedBlockingQueue<Runnable>());
 5     }
 6 
 7     public static ExecutorService newSingleThreadExecutor() {
 8         return new FinalizableDelegatedExecutorService
 9             (new ThreadPoolExecutor(1, 1,
10                                     0L, TimeUnit.MILLISECONDS,
11                                     new LinkedBlockingQueue<Runnable>()));
12     }
13 
14     public static ExecutorService newCachedThreadPool() {
15         return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
16                                       60L, TimeUnit.SECONDS,
17                                       new SynchronousQueue<Runnable>());
18     }

第一种:大小固定的线程池,构造的时候必须告知其最大线程数量(无论如何也不超过这个数字),同时corePoolSize和maximumPoolSize都直接设置为这个最大线程数目参数。keepAliveTime则设置为0。(根本没有“短工”,也就谈不上短工的存活时间了)

重要的来了:为什么阻塞队列是一个无界队列(其实也不完全是无界:Integer.MaxValue)?这其实是设计很有意思的一个地方。我们之前已经说过线程池的运行逻辑。如果把阻塞队列设置为无限大,那么在实际线程数量达到corePoolSize之后,再来的线程都会阻塞在这个队列里面。从而保证:根本就没有“短工”。也就限制了线程池的总大小。

第二种:只持有一个线程的线程池,直接把corePoolSize和maximumPoolSize都设置为1,没啥说的

第三种:线程池是无界的。而阻塞队列是一个SynchronousQueue,这个队列的特点是不存放对象的阻塞队列。每一次take必须先于put,换句话说和别的先放再取的队列不一样,这个队列是先“预订”,等一“到货”,立马“交付”。每一个操作都是先做:然后立马阻塞,等别人对应的操作来取,实现几乎直接的交付。

逻辑变成了:因为核心池大小为0,没有长工。放进来的线程先试图进入SynchronousQueue,如果在此之前有worker做完了自己的工作,去SynchronousQueue拿线程(然后被阻塞),就可以实现直接交付给这个空闲的worker执行。如果失败(没有worker是空闲的),那么因为进入失败会直接新建一个worker,进入maximumPool开始执行。而那些执行完自己task都去SynchronousQueue等60秒,如果还有“订单”要来,就开工干活,否者keepAliveTime到被销毁。

原文地址:https://www.cnblogs.com/dsj2016/p/5844362.html