Java 提供的四种常用线程池解析
既然楼主踩坑就是使用了 JDK 的默认实现,那么再来看看这些默认实现到底干了什么,封装了哪些参数。简而言之 Executors 工厂方法 Executors.newCachedThreadPool() 提供了无界线程池,可以进行自动线程回收;Executors.newFixedThreadPool(int) 提供了固定大小线程池,内部使用无界队列;Executors.newSingleThreadExecutor() 提供了单个后台线程。
详细介绍一下上述四种线程池。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
在 newCachedThreadPool 中如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
初看该构造函数时我有这样的疑惑:核心线程池为 0,那按照前面所讲的线程池策略新任务来临时无法进入核心线程池,只能进入 SynchronousQueue 中进行等待,而 SynchronousQueue 的大小为 1,那岂不是第一个任务到达时只能等待在队列中,直到第二个任务到达发现无法进入队列才能创建第一个线程?
这个问题的答案在上面讲 SynchronousQueue 时其实已经给出了,要将一个元素放入 SynchronousQueue 中,必须有另一个线程正在等待接收这个元素。因此即便 SynchronousQueue 一开始为空且大小为 1,第一个任务也无法放入其中,因为没有线程在等待从 SynchronousQueue 中取走元素。因此第一个任务到达时便会创建一个新线程执行该任务。
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
看代码一目了然了,线程数量固定,使用无限大的队列。再次强调,楼主就是踩的这个无限大队列的坑。
newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
在来看看 ScheduledThreadPoolExecutor()的构造函数
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
ScheduledThreadPoolExecutor 的父类即 ThreadPoolExecutor,因此这里各参数含义和上面一样。值得关心的是 DelayedWorkQueue 这个阻塞对列,在上面没有介绍,它作为静态内部类就在 ScheduledThreadPoolExecutor 中进行了实现。简单的说,DelayedWorkQueue 是一个无界队列,它能按一定的顺序对工作队列中的元素进行排列。
newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级) 执行。
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
首先 new 了一个线程数目为 1 的 ScheduledThreadPoolExecutor,再把该对象传入 DelegatedScheduledExecutorService 中,看看 DelegatedScheduledExecutorService 的实现代码:
DelegatedScheduledExecutorService(ScheduledExecutorService executor) {
super(executor);
e = executor;
}
在看看它的父类
DelegatedExecutorService(ExecutorService executor) {
e = executor;
}
其实就是使用装饰模式增强了 ScheduledExecutorService(1)的功能,不仅确保只有一个线程顺序执行任务,也保证线程意外终止后会重新创建一个线程继续执行任务。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

上一篇: 深入理解 Java 虚拟机
下一篇: Java 懒汉式单例模式
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论