美文网首页Java-多线程
工作三年,小胖不知道线程池的阻塞队列?真的菜!

工作三年,小胖不知道线程池的阻塞队列?真的菜!

作者: JavaFish | 来源:发表于2021-03-25 20:48 被阅读0次

欢迎来到狗哥多线程系列连载。本篇是线程相关的第八篇,前七篇分别是:

创建线程到底有几种方式?

线程有多少种状态?Runnable 一定在执行任务吗?

万字长文,Thread 类源码解析!

wait、notify/notifyAll 解析

线程之生产者消费者模式

狗哥肝了一下午的线程池

线程池的拒绝策略

线程池的内部结构

来源:拉勾教育Java 并发编程.png

如图所示,线程池的内部结构主要由线程池管理器、工作线程、任务队列以及任务四部分组成。

线程池的阻塞队列

先上张图,表格左侧是线程池,右侧为它们对应的阻塞队列,你可以看到 5 种线程池对应了 3 种阻塞队列。

图源:拉勾教育Java 并发编程~阻塞队列.png

下面逐一说下它们的特点:

  • LinkedBlockingQueue,底层是链表结构、采用先进先出原则,默认容量是 Integer.MAX_VALUE,几乎可以认为是无界队列(几乎不可能达到这个数)。而由于 FixedThreadPool 和 SingleThreadExecutor 的线程数是固定的,所以只能用容量无穷大的队列来存任务

  • SynchronousQueue,容量为 0,不做存储,只做转发。由于 CachedThreadPool 的最大线程数是 Integer.MAX_VALUE,有任务提交就转发给线程或者创建新线程来执行,并不需要队列存储任务。所以在自定义使用 SynchronousQueue 的线程池应该把最大线程数设置得尽量大,避免任务数大于最大线程数时,没办法把任务放到队列中也没有足够线程来执行任务的情况。

  • DelayedWorkQueue,内部用的是堆数据结构,初始容量为 16,跟 hashmap 一样动态扩容,对任务延时长短进行排序

为什么不自动创建线程池?

阿里巴巴 Java 规约也约定了,手动创建线程池,效果会更好。为什么呢?回答这个问题之前,先来看看五种线程池初始化的方法:

// FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads, 0 L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue <
        Runnable > ());
}

// SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0 L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue <
        Runnable > ()));
}

首先是 FixedThreadPool 和 SingleThreadExecutor,它两的问题在于都是用默认容量的无界队列 LinkedBlockingQueue,当任务处理慢时,队列迅速积压任务并占用大量内存,发生 OOM(内存溢出)。所以在使用时我们可以根据业务指定队列长度:

ExecutorService threadPool = new ThreadPoolExecutor(2, 5,
    1 L, TimeUnit.SECONDS,
    new LinkedBlockingQueue < > (3),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy());

然后是 CachedThreadPool,也可以发现一个问题:它默认的最大线程是 Integer.MAX_VALUE,当任务贼多时,它就会不断创建线程,而线程执行比较耗时来不及回收。最终也会造成 OOM,所以应该手动指定最大线程数。

// CachedThreadPool
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60 L, TimeUnit.SECONDS, new SynchronousQueue < Runnable > ());
}

最后是 ScheduledThreadPool 和 ScheduledThreadPoolExecutor,这两问题就更大了。首先最大线程数是 Integer.MAX_VALUE,然后阻塞队列是 DelayedWorkQueue,它也是无界队列,最终还是会造成 OOM。

// ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

// ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}

小福利

如果看到这里,喜欢这篇文章的话,请帮点个好看。微信搜索一个优秀的废人,关注后回复电子书送你 100+ 本编程电子书 ,不只 Java 哦,详情看下图。回复 1024送你一套完整的 java 视频教程。

资源

相关文章

  • 工作三年,小胖不知道线程池的阻塞队列?真的菜!

    欢迎来到狗哥多线程系列连载。本篇是线程相关的第八篇,前七篇分别是: 创建线程到底有几种方式?[http://mp....

  • 线程池

    1、为什么要使用线程池2、线程池的工作原理3、线程池参数4、阻塞队列5、饱和策略6、向线程池提交任务7、线程池的状...

  • 线程池

    [TOC] 线程池 1. 并发队列:阻塞队列和非阻塞队列 区别如下: 入队: 非阻塞队列:当队列中满了的时候,放入...

  • Java线程池的工作原理,好处和注意事项

    线程池的工作原理 、一个线程池管理了一组工作线程, 同时它还包括了一个用于放置等待执行任务的任务队列(阻塞队列) ...

  • 线程池

    线程池执行过程 线程池生命周期 线程池分类 阻塞队列 拒绝策略 - ThreadPoolExecutor.Abor...

  • Executors线程池

    newCacheThreadPool(缓存线程池):阻塞队列为SynchronousQueue,核心线程数0,最大...

  • 阻塞队列

    BlockingQueue线程池的数据结构是阻塞队列BlockingQueue。(在多线程领域:所谓阻塞,在某些情...

  • JAVA中常见的阻塞队列详解

    在之前的线程池的介绍中我们看到了很多阻塞队列,这篇文章我们主要来说说阻塞队列的事。 阻塞队列也就是 Blockin...

  • Day 38 并发线程池底层原理详解与源码分析

    任务先放到核心线程池,放不下了再放到阻塞队列,还是放不下就放到最大线程池中. 重点关注,阻塞队列,回收时间实现,拒...

  • 线程池的阻塞队列

    1、线程池中阻塞队列的作用?一般的队列只能保证作为一个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务了...

网友评论

    本文标题:工作三年,小胖不知道线程池的阻塞队列?真的菜!

    本文链接:https://www.haomeiwen.com/subject/pjncpktx.html