线程阀

线程阀:一种线程与线程之间相互制约和交互的机制。

Queue(队列):用于保存一组元素,存取时,遵循先进先出原则

队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。

进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

Deque(双端队列):两端都可以进出的队列,当我们约束从队列的一端进出队时,就形成了一种存取模式,遵循先进后出原则,这就是栈结构。

双端队列主要是用于栈操作,使用栈操作让操作有可追溯性(如:Windows 窗口地址栏内的路径前进栈、后退栈)。

BlockingQueue(阻塞队列):是一个支持两个附加操作的队列。

两个附加操作:

(1)在队列为空时,获取元素的线程会等待队列变成非空。

(2)当队列满时,存储元素的线程会等待队列可用。

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿取元素的线程。

阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿取元素。

BlockingQueue常用5种api:(BlockingQueue不接受null元素,否则会抛出NullPointerException)

(1)add(anObject):把anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则抛出异常。

(2)offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false。

(3)put(anObject):把anObject加到BlockingQueue里,如果BlockingQueue没有空间,则调用此方法的线程被阻断,直到BlockingQueue里面有空间时再继续。

(4)poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则等待time参数规定的时间,取不到时,返回null。

(5)take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的对象被加入为止。

 

数组阻塞队列:ArrayBlockingQueue

ArrayBlockingQueue是一个由数组支持的有界的阻塞队列。此队列按先进先出原则对元素进行排序。

这是典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素,一旦创建了这样的缓存区,就不能再增加其容量。

试图向已满队列中放入元素会导致操作受阻塞,试图从空队列中提取元素将导致类似阻塞。

此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证这种排序,然而,通过将这种公平性(fairness)设置为true而构造的队列允许按照先进先出顺序访问线程,公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。

链表阻塞队列-LinkedBlockingQueue

LinkedBlockingQueue是基于链表的阻塞队列,同ArrayBlockingQueue类似,其内部也维持一个数据缓冲队列(该队列由一个链表构成),

当生产者往队列放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回。

只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程才会被唤醒。反之,对于消费者这端的处理也是同样的原理。

LinkedBlockingQueue之所有能够高效的处理并发数据,还因为其对生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行的操作队列中的数据,以此来提高整个队列的并发性能。

作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE),这样的话,如果生产者的速度一旦大于消费者的速度,也许还没有等待队列满阻塞产生,系统内存就可能消耗尽了。

1 LinkedBlockingQueue<String> bq = new LinkedBlockingQueue<String>(16);

 

优先级阻塞队列-PriorityBlockingQueue

PriorityBlockingQueue是一个支持优先级排序的无界阻塞队列(优先级的判断通过构造函数而传入的Compator对象来决定),但注意的是PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费数据的时候,阻塞数据的消费者。因此使用的时候特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。

在实现PriorityBlockingQueue时,内部控制线程用不的锁采用的是公平锁。

 

延时队列-DelayQueue

DelayQueue是一个支持延时获取元素的,使用优先级队列的实现的,无界阻塞队列。

队列中元素必须现实Delayed接口和Comparable接口,也就是说DelayQueue里面的元素必须有如下两个方法存在:

1 public int comparaTo(To)
2 // 
3 long getDelay(TimeUnit unit)

在创建元素时可以指定多久才能从队列中获取当前元素,只有在延迟期满时才能从队列中提取元素。

运用场景:

(1)缓存系统的设计:

       可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素,表示缓存有效期到了。

(2)定时任务调度:

       使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,比如TimeQueue就是使用DelayQueue实现的。

 

同步队列-SynchronousQueue

SynchronousQueue是一个不存储元素的阻塞队列。

每一个put操作必须等待一个take操作,否则不能继续添加元素。

SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。

队列本身并不存储任何元素,非常适合于传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用。

SynchronousQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue。

声明一个SynchronousQueue有两种不同的方式:

(1)公平模式:如果采用公平模式,SynchronousQueue会采用公平锁,并配合一个先进先出队列来阻塞多余的生产者和消费者。

(2)非公平模式:(SynchronousQueue默认模式)SynchronousQueue采用非公平锁,同时配合一个先进后出队列来管理多余的生产者和消费者。

 

更多线程安全链接:

线程安全

posted @ 2020-07-26 13:30  晕菜一员  阅读(165)  评论(0编辑  收藏  举报