queue体系

阻塞queue:
LinkedBlockingQueue:使用单链表储存元素,默认情况下无界,但也可以规定队列大小,底层主要通过reentrantLock和atomicInteger来保证线程安全,用condition的await 和signal实现阻塞。这里put和take使用了不同的锁,这样在高并发情况下会有更好的性能,这里需要注意一下的是await和signal都需要在上锁的情况下执行,因为await方法主要做了两个事情:一个是用单链表储存需要await的线程,线程设置成park状态。另一个是释放所有锁。signal方法也是做两个事情:判断是否有锁和唤醒await单链表中first node。从上面可以看出await方法和signal方法都需要在有锁情况下执行。

ArrayBlockingQueue:使用数组储存元素,相对于链表来说,数组没有head和tail,所以arrayBlockingQueue定义了putIndex和takeIndex来模拟指针的移动,数组在初始化时length就固定了,所以使用了count来记录元素数量。arrayBlockingQueue在put和take时使用的是同一把锁,所以在性能上会比LinkedBlockingQueue差一些。

SynchronousQueue:是一个不储存元素的queue,在put一个元素之后必须有一个take把元素拿走之后才能再put,这就可以保证线程按顺序执行,比较适合要严格按顺序执行的场景。SynchronousQueue的底层使用自旋加park来实现,使用spins来控制自旋的次数,在自旋次数超出限制时会把当前线程park,有时间限制的spins在少于2核cpu是直接park,大于2核时是自旋32次之后park,没时间限制时在少于2核cpu是直接park,大于2核时时32*16次之后park,这样的设计主要是因为没有时间限制的操作会比有时间限制的操作会更快,因为有时间限制的操作每次自旋时都要检查有没有到时间限制,下面是流程图:
queue体系_第1张图片

LinkedTransferQueue:跟SynchronousQueue实现差不多。
区别是:
SynchronousQueue只提供了阻塞的方法,也就是在put时必须有take才能放回,不然当前线程会阻塞,所以SynchronousQueue是一个没有容量的队列。而LinkedTransferQueue提供了async异步方法,可以把数据放在node队列中,所以LinkedTransferQueue是个可以是个无限队列,跟linkedBlockingQueue差不多,但性能要好的多,LinkedTransferQueue使用自旋和park实现,linkedBlockingQueue使用lock和park实现。
所以linkedTransferQueue是SynchronousQueue和linkedTransferQueue的结合。

PriorityQueue:优先队列,元素需要实现comparable接口或在初始化时传入comparable实现,用来offer进行元素排序,priorityQueue使用的排序算法是最小顶二叉树,参考文献:https://cloud.tencent.com/developer/article/1152628,此算法特点是:
1.Parent节点为n,左child:2n+1.右child:2n+2
2.Child为n,则parent:n/2-1
3.parent节点数据要比两个child都小
4.叶子节点数据是有序的,非叶子节点不保证有序性
5.假设数据总数为n,非叶子节点数量大于n/2,叶子节点数量小于n/2.
queue体系_第2张图片
queue体系_第3张图片

场景一:删除第七个元素:18

在这里插入图片描述

场景二:删除第二个元素:2
在这里插入图片描述

deque系列:是用双链表或数组加head,tail指针实现,可以从头部和尾部添加或删除元素,而queue只能从一头添加或删除元素。
Delayqueue:使用锁的signal和await实现,在每次拿元素时会判断当前元素等待的时间是否已经到了,所以放Delayqueue的元素必须实现Delayed接口

你可能感兴趣的:(java相关)