Java 提供的四种常用线程池解析

发布于 2025-01-10 16:01:23 字数 2946 浏览 7 评论 0

既然楼主踩坑就是使用了 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

情感失落者

暂无简介

文章
评论
28 人气
更多

推荐作者

笑脸一如从前

文章 0 评论 0

mnbvcxz

文章 0 评论 0

真是无聊啊

文章 0 评论 0

旧城空念

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文