一、BlockingQueue
add----当队列满时会抛出异常 remove----当队列空时会抛出异常
offer----当队列满时会返回false peek----当列队空时会返回NULL
put----当队列满时阻塞 take----当队列空时阻塞
1、ArrayBlockingQueue
ArrayBlockingQueue是一个用数组实现的环形队列。在构造函数时需要传入数组大小。其核心就是一把锁加两个条件(非空和非满)。
putIndex代表现在写入的位置。
takeIndex代表读取的位置。
count代表数量。
2、LinkedBlockingQueue
LinkedBlockingQueue是一种基于单向链表的阻塞队列。因为队头和队尾是2个指针分开操作的。所以用了2把锁+2个条件。
AtomicInteger类的count。
在唤醒时需要获得对方的锁。例如,获得putLock,加入阻塞队列,count从0变为1,唤醒take线程,得先获得takeLock。
3、ArrayBlockingQueue和LinkedBlockingQueue的区别
1、ArrayBlockingQueue是基于循环数组实现的且必须指定数组大小,LinkedBlockingQueue是基于链表实现的,不指定链表长度时默认为Integer.MAX_VALUE。
2、ArrayBlockingQueue只用了一把锁+2个条件,LinkedBlockingQueue使用了两把锁+2个条件。
ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。所以ArrayBlockingQueue只用一把锁可能基于性能已经足够优秀,使用两把锁不会带来很大的优化,反而会给代码带来额外的复杂性。
4、PriorityBlockingQueue
按照元素的优先级从小到大出队列的。使用数组实现的小跟堆。只有1把锁+1个条件,没有非满条件,默认初始大小为11,元素超过这个大小之后会自动扩容。
5、DelayQueue
延迟队列,其实就是一个按照延迟时间大小出队的PriorityQueue。放入DelayQueue中的元素必须实现Delayed接口,重写getDelay方法。
(1)getDelay方法
如果getDelay方法的返回值小于等于0,表示该元素到期,需要从队列中拿出来执行。
队列中的元素按照getDelay的值进行排序。
(2)take方法
调用take函数时,如果队列为空或者堆顶元素的延迟时间没到也会阻塞。
使用Thread leader变量记录了等待堆顶元素的第一个线程,通过getDelay可以知道阻塞多长时间,当其他线程发现有leader在等待时会无限期阻塞等待leader拿走后唤醒。
(3)put方法
如果放进去的元素刚好在堆顶,需要将leader设为null,通知等待的线程来任务了,更新leader已经等待时间。如果不在堆顶不需要通知,没有影响。
6、SynchronousQueue
是一种特殊的BlockingQueue,它本身没有容量。先调用put,线程会阻塞,知道另外一个线程调用了take,两个线程才同时解锁,反之亦然。
使用一个TransferQueue的一个单向链表实现,有公平和非公平两种模式。公平模式类似于队列先进先出,非公平模式类似于栈,先进后出。
二、BlockingDeque
定义了一个阻塞的双端队列接口,只有一个实现,LinkedBlockingDeque。
和LinkedBlockingQueue基本一样,区别仅在于LinkedBlockingDeque使用的是双向链表。
三、CopyOnWrite
CopyOnWrite指在写的时候不是直接写源数据,而是把数据拷贝一份进行修改。再通过悲观锁或者乐观锁的方式写回。在读的时候不加锁。
四、ConcurrentLinkedQueue/Deque
首先它是一个单向链表。
在ConcurrentLinkedQueue中,head/tail的更新可能落后于节点的入队和出队,因为它不是直接对head/tail指针进行CAS操作的,而是对Node中item进行操作。
(1)入队列
即使tail指针没有移动,只要对p的next的指针成功进行CAS操作,就算入队列。
每连续追加2个节点,才后移一次tail指针。即使CAS失败也没关系,可以由下1个线程来移动tail指针。
(2)出队列
出队列的判断并不是观察tail指针的位置,而是依赖于head指针后续的节点是否为NULL这一条件。
只要对节点的item执行CAS操作,置为NULL成功则出队列成功,即使head指针没有成功移动也可以由下一个线程继续完成。
(3)队列判空
从head指针开始往后找到第一个不为NULL的节点,如果找到,则队列不为空;如果找不到,则队列为空。