常用的并发集合类,主要有 ArrayBlockingQueue
,ConcurrentLinkedQueue
,LinkedBlockingQueue
,PriorityBlockingQueue
,DelayQueue
,SynchronousQueue
,LinkedBlockingDeque
,以及 COW
系列的集合,如 CopyOnWriteArrayList
,CopyOnWriteArraySet
等
BlockingQueue
描述了一个阻塞队列应该有的行为,例如:
take()
put()
阻塞的读写方法的函数签名上都会抛出 InterruptedException
,具体的阻塞逻辑由实现类自行实现。
是一个基于数组实现的阻塞队列,创建对象时必须指定容量
内置了一个循环队列用于存储数据,可以更高效地利用内存空间
/** The queued items */
final Object[] items;
/** items index for next take, poll, peek or remove */
int takeIndex;
/** items index for next put, offer, or add */
int putIndex;
/** Number of elements in the queue */
int count;
内置一个 ReentrantLock
锁对象,一个队列是否为空的 Condition
对象,以及一个队列是否满的 Condition
对象
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
其中,notEmpty
条件和 notFull
条件都是由 lock
对象创建出来的
public ArrayBlockingQueue(int capacity, boolean fair)
中的 fair
参数来指定公平/非公平模式
fair
参数最终会用于构造 ReentrantLock
锁对象put
方法会在队列满的时候((队尾下标+1
)%队列长度=队头下标))调用 notEmpty
条件进行等待,等队列再次有空位的时候(有元素出队时将会被唤醒)将元素放至队尾take
会在队列为空的时候(队尾下标=队头下标)调用 notFull
条件进行等待,当队列不为空时,将队头的元素返回,并删除队头元素LinkedBlockingQueue
是一个基于单向链表实现的阻塞队列,其容量为:
Integer.MAX_VALUE
,近似无界所以,LinkedBlockingQueue
在不传入容量的情况下,最大容量会被设置为 Integer.MAX_VALUE
/** The capacity bound, or Integer.MAX_VALUE if none */
private final int capacity;
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();
/**
* Head of linked list.
* Invariant: head.item == null
*/
transient Node<E> head;
/**
* Tail of linked list.
* Invariant: last.next == null
*/
private transient Node<E> last;
/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();
/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
LinkedBlockingQueue
底层的数据结构是单向链表ReentrantLock takeLock
,以及由这把锁创建出来的非空条件 Condition notEmpty
ReentrantLock putLock
,以及由这把锁创建出来的非空条件 Condition notFull
take()
方法的时候,会从队列的头部取出元素,所以必须要获取读取锁 takLock
,从而确保同时只有一个线程可以操作链表的头部
take()
方法的时候如果队列为空,那么当前线程将会调用 notEmpty.await()
方法进行阻塞,一直到有元素放入事件发生(如调用 put()
方法)put()
方法的时候,会从队列的尾部添加元素,所以必须要获取到写入锁 putLock
,从而确保同时只有一个线程可以操作链表的尾部
put()
方法的时候如果队列满了,那么当前线程将会调用 notFull.await()
方法进行阻塞,一直到有元素取出事件发生(如调用 take()
方法)remove()
、contains()
等方法时,将会同时获取读、写两把锁,在方法结束后同时释放两把锁
remove()
等方法不能确定在链表的某个位置操作(读取或者新增、删除)元素,所以需要同时对队头和队尾都加锁总的来说,LinkedBlockingQueue
就是通过控制链表同一方向的操作,来实现线程安全和读写条件阻塞;当不能确定操作元素的位置时,将进行双重加锁
ConcurrentLinkedQueue
是一个基于单向链表实现的无界线程安全队列,基于 CAS
+ 自旋来保证线程安全
PriorityBlockingQueue
是一个基于优先级的无界阻塞队列,底层的数据结构是堆。
/**
* Priority queue represented as a balanced binary heap: the two
* children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The
* priority queue is ordered by comparator, or by the elements'
* natural ordering, if comparator is null: For each node n in the
* heap and each descendant d of n, n <= d. The element with the
* lowest value is in queue[0], assuming the queue is nonempty.
*/
private transient Object[] queue;
/**
* The number of elements in the priority queue.
*/
private transient int size;
/**
* The comparator, or null if priority queue uses elements'
* natural ordering.
*/
private transient Comparator<? super E> comparator;
/**
* Lock used for all public operations
*/
private final ReentrantLock lock;
/**
* Condition for blocking when empty
*/
private final Condition notEmpty;
/**
* Spinlock for allocation, acquired via CAS.
*/
private transient volatile int allocationSpinLock;
PriorityBlockingQueue
底层有一个可扩容的对象数组 Object[] queue
,结构是堆ReentrantLock
独占锁,以及由这个锁构造出来的 Condition notEmpty
条件volatile
关键字修饰的整型变量 volatile int allocationSpinLock
size()
方法CAS
尝试把 allocationSpinLock
变量的值修改为 1
64
时,增长为原来的 2
倍 + 2
;否则增长为原来的 1.5
倍allocationSpinLock
的值Comparator
的比较方法进行插入;否则将会使用元素自身的 Comparable.compareTo()
方法进行比较take()
方法时如果队列为空时,当前线程将会调用 notEmpty.await()
进行阻塞,直到有新的元素入队后被唤醒一个不存储元素的阻塞队列,每一个入队操作必须对应一个出队操作,每一个出队操作必须对应一个入队操作。
LinkedBlockingQueue
是一个双端阻塞队列,底层数据结构是双向链表。
ReentrantLock
独占锁,以及由这个锁构造出来的 Condition notEmpty
(队列为空,等待放入) 和 Condition notFull
(队列已满,等待取出)条件take()
)时,将会调用 notEmpty.await()
方法进行阻塞,直到有元素被放入到队列中put()
)时,将会调用 notFull.await()
方法进行阻塞,直到有元素被取出LinkeBlockingQueue
自由实现栈或者队列DelayQueue
是一个无界延时阻塞队列。底层的数据结构是优先级队列 PriorityQueue
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
private Thread leader = null;
/**
* Condition signalled when a newer element becomes available
* at the head of the queue or a new thread may need to
* become leader.
*/
private final Condition available = lock.newCondition();
ReentrantLock
独占锁,以及一个由锁对象创建出来的条件对象 Condition available
PriorityQueue
,是由堆来实现的Thread leader
属性,代表的是当前第一个尝试等待获取队头元素的线程DelayQueue
中的元素,都要实现 Delayed
接口available.await()
方法进行阻塞,直到有新的元素被添加到队列中0
0
,那么说明元素已经到期,直接把这个元素移除并返回long delay
)大于 0
,将会判断当前 leader
是否为空
leader
,并且调用 available.awaitNanos(long delay)
方法,将自己阻塞 long delay
时间available.await()
方法进行无限期等待long delay
时间后,重新将 leader
置为 null
,并且执行上面的逻辑CopyOnWrite
,顾名思义,就是写时复制的意思。
主要的设计思想就是在修改数据的时候,并不直接在原数据上进行修改,而是先拷贝一份原数据的副本,后续对于数据的修改操作在拷贝出来的副本上进行,最后将副本替换掉原数据。
这样做的好处就是,在读取数据的时候,是不用采取任何并发控制手段的,直接访问源数据就行,所以写时复制适用于读多写少的场景。
CopyOnWriteArrayList
是一个并发安全的动态数组,底层数据结构是数组。
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
ReentrantLock
独占锁volatile
关键字修饰的对象数组 array
+1
的新数组,同时把要添加的元素放入到新数组的最后一个位置上,最后把旧数组替换成新数组-1
的新数组,最后根据要删除的元素位置,进行数组拷贝,最后把旧数组替换成新数组CopyOnWriteArrayList
迭代器根本不会,也没有必要去拷贝当前数组CopyOnWriteArraySet
是一个并发安全的无重复集合,底层是基于 CopyOnWriteArrayList
来实现的