线程池运行及拒绝策略

转载:https://blog.csdn.net/swl1993831/article/details/94356564

java中创建线程池的核心是使用

ThredPoolExecutor(int corePoolSize,   int maximumPoolSize,  long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue)
  • (1)当一个任务到来的时候如何它的核心线程数即corePoolSize没满的话,就会创建一个核心线程去执行该任务

  • (2) 如何核心线程数满了,但是阻塞队列没有满的话,就会将该线程先放入阻塞队列中

  • (3) 如果核心线程和阻塞队列都满了,但是最大线程数没有满的话,就会新建一个非核心线程去执行该任务

  • (4)如果核心线程数、阻塞队列、最大线程数都满了的话,就会执行线程池的拒绝策略

拒绝策略,一共有4种方式:
亦可参考线程池的策略

(1)直接丢弃任务
(2)丢弃任务并抛出异常
(3) 将阻塞队列的头节点丢弃,然后尝试将任务放入队列中
(4) 将任务交由主线程即调用者来执行该任务

核心线程是否被回收

在ThreadPoolExecutor类中有一个属性叫 allowCoreThreadTimeOut 默认是false,即核心线程被创建后不会被回收,可以通过它的同名方法将其置为true,此时当核心线程闲置超过空闲时间keepalive后将会被回收掉

核心线程何时被创建

默认情况下当任务到来时才会创建核心线程,不过ThreadPoolExecutor中有两个方法可以提前创建核心线程,一个是preStartAllCoreThread()它会启动所有核心线程,另一个是preStartCoreThread(),这一个的返回值是boolean类型,如果所有的核心线程均已被启动则会返回false,如果一个核心线程启动成功将返回true。

另外需要指出的是,当任务到来时,如果存活的核心线程数小于corePollSize的数量,就算现在有核心线程处于空闲状态,也会新建一个核心线程去执行任务,不会把任务分配给空闲线程;当存活线程数大于corePoolSize时,若此时存在空闲线程,那么将任务分配给空闲线程;在使用有界队列时(为什么单单指出是有界队列,因为无界队列是装不满的,像FixedThreadPool,SingleThreadPool,使用的阻塞队列是LinkedBlockingQueue,可以设置该队列的长度,在这两种线程池创建的过程中长度默认为Integer.Max_Value,是无界的,他们的核心线程数和最大线程数相等,新任务到来时,如果所有核心线程都被创建,且没有空闲线程的话则直接进入阻塞队列等待被执行),若当前线程数大于corePoolSize,但小于maxPoolSzie,此时会把任务放到等待队列中,而不会新建一个线程去执行它,除非此时等待队列已经满了,才会创建一个新线程去执行该任务,如果阻塞队列已满且当前存活线程数为maxPoolSize,则会执行拒绝策略。
拒绝策略的发生条件有两种,除了上面说的,当线程池关闭时,新提交的任务也会触发拒绝,有4种,默认是丢弃任务并抛出异常。
AbortPolicy:默认测策略,丢弃任务并抛出RejectedExecutionException运行时异常;
CallerRunsPolicy:由提交任务的线程执行该任务,并通过反馈机制,减慢提交新任务的速度;
DiscardPolicy:直接丢弃新提交的任务;
DiscardOldestPolicy:如果执行器没有关闭,队列头的任务将会被丢弃,然后执行器重新尝试执行任务(如果失败,则重复这一过程)

扫描二维码关注公众号,回复: 12657634 查看本文章

阻塞队列有3种类型:
直接提交:CacheThreadPool 的阻塞队列为SynchronousQueue,该阻塞队列不存储任务,当任务到来时,调用put方法想将任务放入队列,如果当前没有线程使用take()方法获取任务的话,那么执行put操作的线程将陷入等待,直到有线程调用take(),反之亦然,有线程使用take()相要从队列中获取任务,但是没有线程使用put放入任务,那么它也会阻塞。因此使用该类型的阻塞队列时,maxPoolSize需要使用Integer.max_value,用来避免阻塞任务提交,但是当任务的平均提交速度小于任务的平均执行速度时,会出现线程数会无限增长的问题;
无界队列 :SingleThreadPool、FixedThreadPoll 使用的是LinkedBlockingQueue,使用该值时,当核心线程都创建完毕后,之后到来的任务都会进入阻塞队列中等待执行,导致maxPoolSize无意义,一般与corePoolSize相等即可,它比较适合于互不影响、相互独立的任务,如web等。但是当任务的平均提交速度小于平均执行速度时,会出现队列无限增长的问题。

ScheduledThreadPool它的阻塞队列为DelayedWorkQueue 是一个基于优先级无界阻塞队列,会将距离可执行时间最短的任务放到队列的头部,优先级高

有界队列 如ArrayBlockingQueue,并配合有限的maxPoolSize来控制资源的消耗,但很难控制。使用较大的队列,较小的corePoolSize,可以减少cpu使用率,任务的上下文切换,但是会造成任务的阻塞;使用较小队列,较大的corePoolSize,会使cpu更繁忙。

不管是SynchronousQueue、ArrayBlockingQueue还是LinkedBlockingQueue或DelayedWorkQueue它们都实现了BlockingQueue阻塞队列接口。先进先出,支持泛型

它是线程安全的,使用ReentrantLock加锁,当调用它的put或take方法时都需要获取锁。

它的核心方法分为如下几类

BlockingQueue核心方法
动作种类 抛出异常 返回特定的值 等待 超时
插入节点 add(o) offer(o) put(o) offer(o, time, timeUnit)
删除节点 poll()、remove(o) take() poll(time, timeUnit)
检查 peek(o)
抛出异常:add方法,当阻塞队列为有界且其中元素已满时,抛出异常

返回特定的值:offer 当队列已满时,插入失败返回false、成功返回true;poll和remove为删除队列头节点和指定节点,当队列为空remove返回false,poll返回null,否则分别返回true和元素E;peek获取队列头节点,队列为空时返回null,否则返回E;

等待:put将元素插入队列队尾,当队列已满时,陷入等待,当队列不满notFull condition满足时,被唤醒继续进行插入;take()弹出队列头节点,当队列为空时,陷入等待,当队列不空notEmpty condition满足时被唤醒,继续执行。

超时::如果操作不能马上进行,操作会被阻塞指定的时间,如果指定时间没执行,则返回一个特殊值,一般是true或者false

ArrayBlockingQueue和LinkedBlockingQueue的区别:

1.ArrayBlockingQueue中所有的操作都是使用同一个ReentrantLock锁,且lock可以指定是公平锁还是非公平锁,默认是非公平锁;LinkedBlockingQueue中有两个锁都是非公平的,一个是takelock一个是putkock,它们分别用于插入和移除相关的操作,这样能大大提高吞吐量,当高并发的情况下,生产者和消费者能并行操作队列中的数据,提高并发性能。

2.ArrayBlockingQueue是有界的,创建时需要指定长度;LinkedBlockingQueue既可以是无界的又可以是有界的,默认长度是max,无界

3.ArrayBlockingQueue是使用数组存储元素节点,LinkedBlockingQueue是使用node节点链表的形式

https://blog.csdn.net/weixin_37598682/article/details/79895769
https://www.jianshu.com/p/c41e942bcd64 有个图
https://www.cnblogs.com/drizzlewithwind/p/7707471.html

猜你喜欢

转载自blog.csdn.net/qq_39900031/article/details/113914920