线程池运行及拒绝策略

java中创建线程池的核心是使用ThredPoolExecutor(int corePoolSize,   int maximumPoolSize,  long keepAliveTime,TimeUnit unit,                               BlockingQueue<Runnable> workQueue)

(1)当一个任务到来的时候如何它的核心线程数即corePoolSize没满的话,就会创建一个核心线程去执行该任务

  (2)   如何核心线程数满了,并仍有任务提交,则将任务先放入阻塞队列中

  (3)   阻塞队列已无空闲,还有任务继续提交,若最大线程数没有满的话,则会新建非核心线程并分配任务

  如果核心线程数、阻塞队列、最大线程数都满了的话,就会执行线程池的拒绝策略,一共有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:如果执行器没有关闭,队列头的任务将会被丢弃,然后执行器重新尝试执行任务(如果失败,则重复这一过程)


阻塞队列有3种类型:
直接提交:CacheThreadPool 的阻塞队列为SynchronousQueue<Runable>,该阻塞队列不存储任务,当任务到来时,调用put方法想将任务放入队列,如果当前没有线程使用take()方法获取任务的话,那么执行put操作的线程将陷入等待,直到有线程调用take(),反之亦然,有线程使用take()相要从队列中获取任务,但是没有线程使用put放入任务,那么它也会阻塞。因此使用该类型的阻塞队列时,maxPoolSize需要使用Integer.max_value,用来避免阻塞任务提交,但是当任务的平均提交速度大于任务的平均执行速度时,会出现线程数会无限增长的问题;
无界队列 :SingleThreadPool、FixedThreadPoll 使用的是LinkedBlockingQueue<Runable>,使用该值时,当核心线程都创建完毕后,之后到来的任务都会进入阻塞队列中等待执行,导致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节点链表的形式

阻塞队列BlockingQueue及其子类的使用_Lyzxii的博客-CSDN博客_queue子类
https://www.jianshu.com/p/c41e942bcd64 有个图
https://www.cnblogs.com/drizzlewithwind/p/7707471.html

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值