精华内容
下载资源
问答
  • 讲述线程池原理,线程池使用场景和注意事项,手动创建线程池方法,注意事项,阻塞队列的相关知识
  • 并发-线程池阻塞队列 并发-线程池阻塞队列 并发-线程池阻塞队列
  • 一般的队列只能保证作为一个有限长度的缓冲区,如果超过了...阻塞队列自带阻塞和唤醒功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂起,从而维持核心线程的存活,不至于一致占用cpu资源。 ...

    一般的队列只能保证作为一个有限长度的缓冲区,如果超过了缓冲长度,就无法保留当前的任务了,阻塞队列通过阻塞可以保留住当前想要继续入列的任务。

    阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源

    阻塞队列自带阻塞和唤醒功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂起,从而维持核心线程的存活,不至于一致占用cpu资源。

    展开全文
  • 线程池中的阻塞队列选择

    万次阅读 多人点赞 2019-02-11 16:04:41
    内存被耗尽可能有一个原因是,因为使用了 newFixedThreadPool 线程池,而它的工作机制是,固定了N个线程,而提交给线程池的任务队列是不限制大小的,如果Kafka发消息被阻塞或者变慢,那么显然队列里面的内容会越来越...

    这是一个十分严重的问题

    自从最近的某年某月某天起,线上服务开始变得不那么稳定。在高峰期,时常有几台机器的内存持续飙升,并且无法回收,导致服务不可用。

    例如GC时间采样曲线:

    和内存使用曲线:

    图中所示,18:50-19:00的阶段,已经处于服务不可用的状态了。上游服务的超时异常会增加,该台机器会触发熔断。熔断触发后,改台机器的流量会打到其他机器,其他机器发生类似的情况的可能性会提高,极端情况会引起所有服务宕机,曲线掉底。

    因为线上内存过大,如果采用 jmap dump的方式,这个任务可能需要很久才可以执行完,同时把这么大的文件存放起来导入工具也是一件很难的事情。再看JVM启动参数,也很久没有变更过 Xms, Xmx, -XX:NewRatio, -XX:SurvivorRatio, 虽然没有仔细分析程序使用内存情况,但看起来也无大碍。

    于是开始找代码,某年某天某月~ 嗯,注意到一段这样的代码提交:

    private static ExecutorService executor = Executors.newFixedThreadPool(15);
    public static void push2Kafka(Object msg) {
        executor.execute(new WriteTask(msg,  false));    
    }
    

    相关代码的完整功能是,每次线上调用,都会把计算结果的日志打到 Kafka,Kafka消费方再继续后续的逻辑。内存被耗尽可能有一个原因是,因为使用了 newFixedThreadPool 线程池,而它的工作机制是,固定了N个线程,而提交给线程池的任务队列是不限制大小的,如果Kafka发消息被阻塞或者变慢,那么显然队列里面的内容会越来越多,也就会导致这样的问题。

    为了验证这个想法,做了个小实验,把 newFixedThreadPool 线程池的线程个数调小一点,例如 1。果然压测了一下,很快就复现了内存耗尽,服务不可用的悲剧。

    最后的修复策略是使用了自定义的线程池参数,而非 Executors 默认实现解决了问题。下面就把线程池相关的原理和参数总结一下,避免未来踩坑。

    1. Java线程池

    虽然Java线程池理论,以及构造线程池的各种参数,以及 Executors 提供的默认实现之前研读过,不过线上还没有发生过线程池误用引发的事故,所以有必要把这些参数再仔细琢磨一遍。

    优先补充一些线程池的工作理论,有助于展开下面的内容。线程池顾名思义,就是由很多线程构成的池子,来一个任务,就从池子中取一个线程,处理这个任务。这个理解是我在第一次接触到这个概念时候的理解,虽然整体基本切入到核心,但是实际上会比这个复杂。例如线程池肯定不会无限扩大的,否则资源会耗尽;当线程数到达一个阶段,提交的任务会被暂时存储在一个队列中,如果队列内容可以不断扩大,极端下也会耗尽资源,那选择什么类型的队列,当队列满如何处理任务,都有涉及很多内容。线程池总体的工作过程如下图:

    线程池内的线程数的大小相关的概念有两个,一个是核心池大小,还有最大池大小。如果当前的线程个数比核心池个数小,当任务到来,会优先创建一个新的线程并执行任务。当已经到达核心池大小,则把任务放入队列,为了资源不被耗尽,队列的最大容量可能也是有上限的,如果达到队列上限则考虑继续创建新线程执行任务,如果此刻线程的个数已经到达最大池上限,则考虑把任务丢弃。

    在 java.util.concurrent 包中,提供了 ThreadPoolExecutor 的实现。

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler) {
    } 
    

    既然有了刚刚对线程池工作原理对概述,这些参数就很容易理解了:

    corePoolSize- 核心池大小,既然如前原理部分所述。需要注意的是在初创建线程池时线程不会立即启动,直到有任务提交才开始启动线程并逐渐时线程数目达到corePoolSize。若想一开始就创建所有核心线程需调用prestartAllCoreThreads方法。

    maximumPoolSize-池中允许的最大线程数。需要注意的是当核心线程满且阻塞队列也满时才会判断当前线程数是否小于最大线程数,并决定是否创建新线程。

    keepAliveTime - 当线程数大于核心时,多于的空闲线程最多存活时间

    unit - keepAliveTime 参数的时间单位。

    workQueue - 当线程数目超过核心线程数时用于保存任务的队列。主要有3种类型的BlockingQueue可供选择:无界队列,有界队列和同步移交。将在下文中详细阐述。从参数中可以看到,此队列仅保存实现Runnable接口的任务。 别看这个参数位置很靠后,但是真的很重要,因为楼主的坑就因这个参数而起,这些细节有必要仔细了解清楚。

    threadFactory - 执行程序创建新线程时使用的工厂。

    handler - 阻塞队列已满且线程数达到最大值时所采取的饱和策略。java默认提供了4种饱和策略的实现方式:中止、抛弃、抛弃最旧的、调用者运行。将在下文中详细阐述。

    2. 可选择的阻塞队列BlockingQueue详解

    在重复一下新任务进入时线程池的执行策略: 
    如果运行的线程少于corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存入queue中,而是直接运行) 
    如果运行的线程大于等于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程。 
    如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。 
    主要有3种类型的BlockingQueue:

    无界队列

    队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列做为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。阅读代码发现,Executors.newFixedThreadPool 采用就是 LinkedBlockingQueue,而楼主踩到的就是这个坑,当QPS很高,发送数据很大,大量的任务被添加到这个无界LinkedBlockingQueue 中,导致cpu和内存飙升服务器挂掉。

    有界队列

    常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。 
    使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。

    在我们的修复方案中,选择的就是这个类型的队列,虽然会有部分任务被丢失,但是我们线上是排序日志搜集任务,所以对部分对丢失是可以容忍的。

    同步移交队列

    如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列

    3. 可选择的饱和策略RejectedExecutionHandler详解

    JDK主要提供了4种饱和策略供选择。4种策略都做为静态内部类在ThreadPoolExcutor中进行实现。

    3.1 AbortPolicy中止策略

    该策略是默认饱和策略。

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                throw new RejectedExecutionException("Task " + r.toString() +
                                                     " rejected from " +
                                                     e.toString());
     } 
    

    使用该策略时在饱和时会抛出RejectedExecutionException(继承自RuntimeException),调用者可捕获该异常自行处理。

    3.2 DiscardPolicy抛弃策略

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
    

    如代码所示,不做任何处理直接抛弃任务

    3.3 DiscardOldestPolicy抛弃旧任务策略

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                if (!e.isShutdown()) {
                    e.getQueue().poll();
                    e.execute(r);
                }
    } 
    

    如代码,先将阻塞队列中的头元素出队抛弃,再尝试提交任务。如果此时阻塞队列使用PriorityBlockingQueue优先级队列,将会导致优先级最高的任务被抛弃,因此不建议将该种策略配合优先级队列使用。

    3.4 CallerRunsPolicy调用者运行

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                if (!e.isShutdown()) {
                    r.run();
                }
    } 
    

    既不抛弃任务也不抛出异常,直接运行任务的run方法,换言之将任务回退给调用者来直接运行。使用该策略时线程池饱和后将由调用线程池的主线程自己来执行任务,因此在执行任务的这段时间里主线程无法再提交新任务,从而使线程池中工作线程有时间将正在处理的任务处理完成。

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

    既然楼主踩坑就是使用了 JDK 的默认实现,那么再来看看这些默认实现到底干了什么,封装了哪些参数。简而言之 Executors 工厂方法Executors.newCachedThreadPool() 提供了无界线程池,可以进行自动线程回收;Executors.newFixedThreadPool(int) 提供了固定大小线程池,内部使用无界队列;Executors.newSingleThreadExecutor() 提供了单个后台线程。

    详细介绍一下上述四种线程池。

    4.1 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中取走元素。因此第一个任务到达时便会创建一个新线程执行该任务。

    4.2 newFixedThreadPool

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

    看代码一目了然了,线程数量固定,使用无限大的队列。再次强调,楼主就是踩的这个无限大队列的坑。

    4.3 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是一个无界队列,它能按一定的顺序对工作队列中的元素进行排列。

    4.4 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)的功能,不仅确保只有一个线程顺序执行任务,也保证线程意外终止后会重新创建一个线程继续执行任务。

    以上除了优化线程池,更应该优化kafka队列,设置相应的超时机制,避免超长时间阻塞等待,导致OOM。

     

    Alibaba命名规范的解释:

    【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明: Executors 返回的线程池对象的弊端如下:

    1) FixedThreadPool 和 SingleThreadPool : 允许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而导致 OOM 。

    2) CachedThreadPool 和 ScheduledThreadPool : 允许的创建线程数量为 Integer.MAX_VALUE ,可能会创建大量的线程,从而导致 OOM 。

     

    转自: https://zhuanlan.zhihu.com/p/32867181  略有改变。

    展开全文
  • 线程池 线程池介绍 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价...

    线程池

    线程池介绍

    一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。

    可以通俗理解为,在一个容器中存储了一些已经存在的线程,再有请求进来的时候可以直接调用,而不用创建新的先城市里,同时可以管理线程任务的调度和流程。

    线程池的最上层接口是Executor,这个接口定义了一个核心方法execute(Runnabel command),这个方法最后被ThreadPoolExecutor类实现,这个方法是用来传入任务的。而且ThreadPoolExecutor是线程池的核心类,此类的构造方法如下:

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
    BlockingQueue<Runnable> workQueue);
     
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
    BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
     
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
    BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
     
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
    BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
    

    构造函数参数:

    • corePoolSize:核心线程数量
    • maximumPoolSize:线程不够用时能够创建的最大线程数
    • workQueue:任务等待队列,使用BlockingQueue阻塞队列
    • keepAliveTime:当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁。
      • unit :keepAliveTime 参数的时间单位。
    • threadFactory:创建新线程,Executors.defaultThreadFactory()
      • 通过复写其newThread方法,实现创建线程。
    • handler:线程池饱和策略
      • AbortPolicy:直接抛出异常,默认策略
      • CallerRunsPolicy:调用执行自己的线程运行任务。您不会任务请求。但是这种策略会降低对于新任务提交速度,影响程序的整体性能。另外,这个策略喜欢增加队列容量。如果您的应用程序可以承受此延迟并且你不能任务丢弃任何一个任务请求的话,你可以选择这个策略。
      • DiscardPolicy :不处理新任务,直接丢弃掉。
      • DiscardOldestPolicy:此策略将丢弃最早的未处理的任务请求。
      • 实现RejectedExecutionHandler接口自定义handler

    线程池的运行流程

    在这里插入图片描述
    在这里插入图片描述

    线程池的状态

    • RUNNING:能接受新提交的任务,并且也能处理阻塞队列中的任务,
    • SHUTDOWN:不再接受新提交的任务,但可以处理存量任务
    • STOP:不再接受新提交的任务,也不处理存量任务
    • TIDYING:所有任务都已终止
    • TERMINATED:terminated方法执行完后进入该状态

    线程池的大小如何选定

    • CPU密集型:线程数=按照核数或者核数+1设定
    • I/O密集型:线程数=CPU核数*(1+平均等待时间/平均工作时间)

    java类中现有的线程池

    我们也可以使用Executor的静态方法,创建java中已有的线程池。
    在这里插入图片描述

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    

    在《阿里巴巴 Java 开发手册》“并发处理”这一章节,明确指出线程资源必须通过线程池提供,不允许在应用中自行显示创建线程。

    为什么呢?

    使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源开销,解决资源不足的问题。如果不使用线程池,有可能会造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

    另外《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

    Executors 返回线程池对象的弊端如下:

    • FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
    • CachedThreadPool 和ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。

    阻塞队列

    BlockingQueue:提供可阻塞的入队和出队操作

    • 主要用于生产者-消费者模式,在多线程场景时生产者线程在队列尾部添加元素,而消费者线程则在队列头部消费元素,通过这种方式能够达到将任务的生产和消费进行隔离的目的。
      在这里插入图片描述

    下面主要介绍一下:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,这三个 BlockingQueue 的实现类。

    ArrayBlockingQueue

    ArrayBlockingQueue 是 BlockingQueue 接口的有界队列实现类,底层采用数组来实现。ArrayBlockingQueue 一旦创建,容量不能改变。其并发控制采用可重入锁来控制,不管是插入操作还是读取操作,都需要获取到锁才能进行操作。当队列容量满时,尝试将元素放入队列将导致操作阻塞;尝试从一个空队列中取一个元素也会同样阻塞。

    ArrayBlockingQueue 默认情况下不能保证线程访问队列的公平性,所谓公平性是指严格按照线程等待的绝对时间顺序,即最先等待的线程能够最先访问到 ArrayBlockingQueue。而非公平性则是指访问 ArrayBlockingQueue 的顺序不是遵守严格的时间顺序,有可能存在,当 ArrayBlockingQueue 可以被访问时,长时间阻塞的线程依然无法访问到 ArrayBlockingQueue。如果保证公平性,通常会降低吞吐量。如果需要获得公平性的 ArrayBlockingQueue,可采用如下代码:

    private static ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(10,true);
    
    LinkedBlockingQueue

    LinkedBlockingQueue 底层基于单向链表实现的阻塞队列,可以当做无界队列也可以当做有界队列来使用,同样满足 FIFO 的特性,与 ArrayBlockingQueue 相比起来具有更高的吞吐量,为了防止 LinkedBlockingQueue 容量迅速增,损耗大量内存。通常在创建 LinkedBlockingQueue 对象时,会指定其大小,如果未指定,容量等于 Integer.MAX_VALUE。
    相关构造方法:

        /**
         *某种意义上的无界队列
         * Creates a {@code LinkedBlockingQueue} with a capacity of
         * {@link Integer#MAX_VALUE}.
         */
        public LinkedBlockingQueue() {
            this(Integer.MAX_VALUE);
        }
    
        /**
         *有界队列
         * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
         *
         * @param capacity the capacity of this queue
         * @throws IllegalArgumentException if {@code capacity} is not greater
         *         than zero
         */
        public LinkedBlockingQueue(int capacity) {
            if (capacity <= 0) throw new IllegalArgumentException();
            this.capacity = capacity;
            last = head = new Node<E>(null);
        }
    
    PriorityBlockingQueue

    PriorityBlockingQueue 是一个支持优先级的无界阻塞队列。默认情况下元素采用自然顺序进行排序,也可以通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则。

    PriorityBlockingQueue 并发控制采用的是 ReentrantLock,队列为无界队列(ArrayBlockingQueue 是有界队列LinkedBlockingQueue 也可以通过在构造函数中传入 capacity 指定队列最大的容量,但是 PriorityBlockingQueue 只能指定初始的队列大小,后面插入元素的时候,如果空间不够的话会自动扩容)。

    简单地说,它就是 PriorityQueue 的线程安全版本。不可以插入 null 值,同时,插入队列的对象必须是可比较大小的(comparable),否则报 ClassCastException 异常。它的插入操作 put 方法不会 block,因为它是无界队列(take 方法在队列为空的时候会阻塞)。

    阻塞队列详解转自javaGuide
    线程池部分转自

    展开全文
  • 线程池各类区别使用场景, 工作久了才知道理论的重要性。
  •   1、阻塞队列作用:一个是当一般队列中的任务满了后,...  阻塞队列自带阻塞和唤醒功能,不需要额外的处理,无任务时线程池利用阻塞队列的take方法挂起,从而保证核心线程的存货,不至于一直占用CPU资源。 ...

      1、阻塞队列的作用:一个是当一般队列中的任务满了后,阻塞队列可以保留之后的任务;另一个作用是,当队列没有任务时阻塞获取任务的线程、使其进入wait状态,释放CPU资源。
      阻塞队列自带阻塞和唤醒功能,不需要额外的处理,无任务时线程池利用阻塞队列的take方法挂起,从而保证核心线程的存货,不至于一直占用CPU资源。
    在这里插入图片描述

    展开全文
  • 线程池阻塞队列

    2020-07-27 11:23:29
    由于频繁的创建和销毁线程会消耗很多资源,因此线程池应运而生来去除频繁的创建与删除线程这一过程。 2、常见线程池 ①、newSingleThreadExecutor 单一线程池,使用唯一的工作线程执行任务,保证所有任务按照指定...
  • 线程池常用的阻塞队列有哪些?

    千次阅读 2020-06-25 05:51:27
    线程池常用的阻塞队列有哪些? 文章目录线程池常用的阻塞队列有哪些?1.线程池的内部结构2.阻塞队列3.LinkedBlockingQueue4.SynchronousQueue5.DelayedWorkQueue6.参考 1.线程池的内部结构 线程池内部由四部分组成 ...
  • 一、线程池定义和使用 jdk 1.5 之后就引入了线程池。 1.1 定义 从上面的空间切换看得出来,线程是稀缺资源,它的创建与销毁是一个相对偏重且耗资源的操作,而Java线程依赖于内核线程,创建线程需要进行操作系统状态...
  • 一,线程池 首先了解线程池整个框架 1,这里记住最核心的类是ThreadPoolExecutor 2,在ExecuorService中提供了newSingleThreadExecutor,newFixedThreadPool,newCacheThreadPool,newScheduledThreadPool四个...
  • 线程池阻塞队列

    2021-07-26 22:21:48
    阻塞队列 add/remove 如果队列满了,add(T)会抛出异常 如果队列为空,remove()会抛出异常 offer/poll 如果队列满了,offer(T)会返回false 如果队列为空,poll()会返回null put/take 如果队列满了,put(T)会...
  • Java线程池阻塞队列

    千次阅读 2017-02-20 10:05:32
    Java提供了自己的线程池。每次只执行指定数量的线程,java.util.concurrent.ThreadPoolExecutor 就是这样的线程池。ThreadPoolExecutor参数介绍: corePoolSize 核心线程数,指保留的线程池大小(不超过...
  • 阻塞队列 实现了BlockingQueue接口 阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。 ...
  • 线程池阻塞队列 1 SynchronousQueue 无缓冲阻塞队列: 该队列每个插入操作必须等待另一个线程进行相应的操作,本身不存储数据,只有当前一个线程删除时,后一个线程才能被删除。 2 ArrayBlokingQueue 有界阻塞...
  • 线程池&阻塞队列实现--笔记

    千次阅读 2018-08-23 08:58:42
    参考链接: ... http://tutorials.jenkov.com/java-concurrency/thread-pools.html 阻塞队列 阻塞队列是一个可以阻塞线程的队列。当你尝试向空队列中弹出元素时,会被阻塞,直到入队一个新元素。当向满队列写入...
  • 线程池中为什么要使用阻塞队列

    千次阅读 2020-05-24 22:11:26
    线程池创建线程需要获取mainlock这个全局锁,影响并发效率,阻塞队列可以很好的缓冲。 如果新任务的到达速率超过了线程池的处理速率,那么新到来的请求将累加起来,这样的话将耗尽资源。 在一个task提交到线程池...
  • Java线程池带实例讲解,阻塞队列说明 首先,线程池是啥,有啥好处这里就不提了.google一下马上知道. 嘻嘻嘻 首先第一步!我们先来认识下 在 java.util.concurrent 包中,提供了 ThreadPoolExecutor 的实现。 该类是核心,...
  • java线程池常用的阻塞队列

    千次阅读 2020-07-22 16:46:03
    表格左侧是线程池,右侧为它们对应的阻塞队列,可以看到 5 种线程池对应了 3 种阻塞队列,我们接下来对它们进行逐一的介绍。 1. LinkedBlockingQueue 对于 FixedThreadPool 和 SingleThreadExector 而言,它们使用的...
  • 三种阻塞队列:    SynchronousQueue    ArrayBlockingQueue    LinkedBlockingQueue SynchronousQueue      无缓冲无界等待队列...
  • Java 线程池等待队列问题

    千次阅读 2019-09-20 15:38:48
    线程池的等待队列最大长度默认为int的最大值,随口默写出来就是2147483647(2^31 -1,高中物理老师说过一句话,记住一些固定的数字可以预判一些问题)。线程池在提交任务时,如果线程池未达到最大线程数,则起线程...
  • 工作中有个任务需要写多线程记录一下登陆日志,...我很常规的设置了这些参数,核心线程数:5 最大线程数:Integer.MaxValue 存活时间:60s 阻塞队列:synBlockinQueue() 饱和策略重写的 后来code review的时候发现阻塞...
  • 使用线程池比手动创建线程好在哪里? 手工创建线程,每一个任务都创建线程问题: 第一点,反复创建线程系统开销比较大,每个线程创建和销毁都需要时间,如果任务比较简单,那么就有可能导致创建和销毁线程消耗的...
  • 线程 线程的状态 Java线程在运行的声明周期中可能会处于6种不同的状态,这6种线程状态分别为如下所示。...表示线程被锁阻塞,它暂时不活动。 • Waiting:等待状态。线程暂时不活动,并且不运行任何代
  • 一、线程池阻塞队列、 二、拒绝策略、 三、使用 ThreadPoolExecutor 自定义线程池参数、
  • 之前笔记有记录java线程池的拒绝策略,回顾一下线程池的处理任务的优先级: 先考虑corePoolSize、任务队列(缓冲队列)workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。 即: ...
  • 注意这个ConcurrentLinkedQueue这个是个非阻塞队列, 这个队列是没有长度限制的,不用给一个长度,直接,向队列不停的.add数据就可以, 然后poll,是出队列,出队列的同时,并且会从队列中移出元素 然后peek,会返回队列...
  • 组,以便缓存队列中的数据对象,这是一个常用的阻塞队列,除了一个定长数 组外,ArrayBlockingQueue 内部还保存着两个整形变量,分别标识着队列的 头部和尾部在数组中的位置。 ArrayBlockingQueue 在生产者放入数据...
  • 在设置线程池队列长度时,如果长度设置的不合理就无法发挥出多线程的威力。设置线程池队列长度取决于使用场景;比如全程异步的系统,队列可以设置为0,corePoolSize设置为cpu核数。研究tomcat、Dubbo等业界成熟的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 100,264
精华内容 40,105
关键字:

线程池阻塞队列的作用