阻塞队列模型和线程池

阻塞队列模型介绍java

阻塞队列模型和线程池息息相关,所以本篇博客先介绍阻塞队列的相关知识。以下图所示:
数组

wKiom1b2RQ2SNHWrAAAu0KDMMdQ915.png

首先咱们来讲,什么是Queue,而后在谈什么是BlockingQueue。
缓存

那么什么是Queue呢?一句话,就是一端进,另外一端出,这样就造成了First In , First Out,即先进先出。而BlockingQueue只不过是在Queue的基础上进行了2个附加操做而已:若是Queue空,那么Out线程阻塞,若是Queue满,那么In线程阻塞。并发


理解了上面的Queue/BlockingQueue,那么就好理解Deque/BlockingDeque了。ide

Deque,就是双端队列,其实就是说在2端,均可以进行IN/OUT,固然若是咱们只在同一端进行IN/OUT,那么天然造成了栈结构(先进后出)。高并发


其次,咱们先来看看java.util.concurrent.BlockingQueue的API列表:spa

wKiom1b2RsSBMEufAAAd4AxQv2M881.png

其实从这里能够看出JAVA API的一个思路,每每一个操做提供多种选择:线程

若是队列是空的,那么消费线程该如何处理呢?3d

能够马上抛出异常,也能够返回false/NULL,也能够过一段时间在TRY...blog


好了,到这里,咱们只看到了接口API,若是要你来实现,你会怎么作呢?又会有哪些疑问呢?


思考:

是否应该提供一个存储,来放置队列中的元素呢?

这个存储应该多大呢?能够是什么形式呢?

队列中的元素是否存在优先级排序呢?

对队列中的元素进行操做时,是否应该有锁的控制呢?

一端IN,另外一端OUT,这2个操做能够同时进行吗?


带着这些疑问,咱们来对典型的BlockingQueue来进行分析。


ArrayBlockingQueue PK LinkedBlockingQueue


存储PK:

从名称上就能够知道,一个用的是数组,另外一个用的是链表。看看源码验证下:


ArrayBlockingQueue:

wKiom1b2TBqykUFqAAAHWCwuOfk038.png


LinkedBlockingQueue:

wKiom1b2TDGTfNZ7AAArcPBhXaU329.png


容量PK:

ArrayBlockingQueue能够经过构造方法指定容量:

wKioL1b2TWqAkAZEAAAilXAs8XU931.png

而LinkedBlockingQueue若是在初始化时不指定容量,那么将是Integer.MAX_VALUE,这一点很重要,特别是在生产者的速度大于消费者的速度,因为此时无容量限制,将致使队列中的元素开始膨胀,那么将消耗掉大量系统资源。

wKioL1b2TiPjk5wWAABUl54WecY009.png


锁PK:

在ArrayBlockingQueue中只有一个锁:

wKiom1b2TdKAEXG_AAAJ8rP7w4w494.png

而在LinkedBlockingQueue中有2个锁:

wKioL1b2TqzhaX9HAAANfu9FdwE655.png

wKiom1b2TjeAQpuMAAAL2dVdzdM519.png

其实到这里,咱们已经能够大体猜想出,LinkedBlockingQueue对于take/put使用了分别的锁,从而比ArrayBlockingQueue在高并发下更具优点。


咱们再来看看其余BlockingQueue:

DelayQueue:延迟队列,其实是说,队列中的元素生效的话,有个时间差。

PriorityQueue:优先级队列,会提供Comparator来进行队列中的元素的排序。

SynchronousQueue:这个队列比较特殊,由于没有存储机制,实际上只是作了一个生产者和消费者的传递机制。


线程池介绍

若是任务到达时,才开始建立线程,这实际上会让任务的执行被延迟,因而产生了线程池的概念,若是在池子中已经存在了一批线程,那么任务到达时天然省去了线程建立的时间,至关于提升了响应速度。其次,若是线程执行完任务后,在放入池子中,这至关于在复用线程,达到了资源节约的目的。固然,若是任务的执行时间是远远大于线程的建立/销毁时间,其实就无所谓了。



快速建立线程池:Executors

wKiom1b2Vsyg8igYAAAxDv6DG20760.png


Executors提供了一系列的快速建立线程池的方法,好比:

wKioL1b2V-TBFjcAAAAdWmjC9AM355.png



wKiom1b2V2XwcB4ZAAAiRV0Onrs008.png


wKioL1b2WCDwT9pPAAAc9lhRhko375.png


建立数量固定的/单个的/缓存的  线程池。

能够看到线程池的建立利用到了上面说起的BlockingQueue,队列中的元素就是任务Runnable。

方法返回的都是ExecutorService的实现类:ThreadPoolExecutor。



线程池的核心:ThreadPoolExecutor

咱们直接来看看ThreadPoolExecutor的构造方法:

wKiom1b2WLOCpoqmAABhcZlS7Bk959.png

理解这些参数,对于理解线程池的原理有很大帮助:


corePoolSize:线程池的核心线程数量,是线程数目的一个稳定峰值。


maximumPoolSize:线程池的最大线程数量,若是corePoolSize依旧知足不了须要,那么可让线程增加至maximumPoolSize,一旦须要降低,那么超出核心线程的那一部分线程资源将被回收。


workQueue:这个队列是待处理的任务队列。实际上,在ThreadPoolExecutor中除此以外还存在一个正在处理的工做队列workers

wKioL1b2e2yDfnD9AAAI_i_YhEc158.png

keepAliveTime:超过核心线程数,又小于最大线程数目的线程在空闲的状况下,多久回收。


threadFactory:线程工厂,实际上用的是默认的DefaultThreadFactory,经过代码发现仅仅是针对Thread作了些设置(好比线程组/线程名称/后台/优先级等设置),将Runnable挂到Thread上而已。


handler:若是已经达到了最大线程数目,那么对于任务只能开始拒绝了,这个就是拒绝处理的策略类。

相关文章
相关标签/搜索