Impl |
ADT |
Data Structure |
Performance (Big O notation) |
ArrayBlockingQueue |
BlockingQueue |
Array of objects. Bounded. Supports fairness policy. If fairness=true, threads are guaranteed to access in FIFO order. A classic bounded buffer. |
offer, peek, poll, size:O(1) put, take: O(1) if ignore blocking time. Fairness generally decreases throughput but avoids producer/consumer starvation. |
LinkedBlockingQueue |
BlockingQueue |
Linked structure. optionally-bounded |
offer, peek, poll, size:O(1) put, take uses separate locks, higher throughput. Even more throughput if unbounded (no bound blocking, hence more data passing through). It has less predictable performance because of dynamic memory allocation and fragmentation (hold locks longer). |
ConcurrentLinkedQueue |
Queue |
Linked structure. Unbounded. Not-blocking but still thread-safe. Null not permitted. Appropriate choice over LinkedBlockingQueue when many threads access the collection. |
The methods employs an efficient "wait-free" algorithm. offer, peek, poll:O(1) size: O(n) because of the asynchronous nature. size method is not very useful in concurrent applications: if elements are added or removed during execution of this method, the returned result may be inaccurate. Iterators are weakly consistent (don't throw ConcurrentModificationException and don't show the changes consistently to other threads). |
DelayQueue |
BlockingQueue |
Internally, it uses a PriorityQueue instance which in turn uses Binary Heap data structure. Unbounded. Each element has to implement java.util.concurrent.Delayed interface. When the method Delayed.getDelayed(TimeUnit unit) returns a value less than or equal to zero, the parent element becomes available in the queue for removing/polling, Regardless available or not, the element is still an element of the queue e.g. size() includes it. It doesn't permit null. |
offer, poll, remove() and add: O(log n) remove(Object), contains(Object) O(n) peek, element, and size: O(1) |
PriorityBlockingQueue |
BlockingQueue |
Binary Heap. Unbounded Uses the same ordering rules as PriorityQueue i.e. Elements are ordered to their natural ordering or by a Comparator provided at construction time. |
offer, poll, remove() and add: O(log n) remove(Object), contains(Object) O(n) peek, element, and size: O(1) put: O(log n). As the queue is unbounded this method never blocks. take: O(log n). This method blocks until an element becomes available. |
SynchronousQueue |
BlockingQueue |
No standard data structure. Applies rendezvous channels pattern where one thread handoff data/info to another thread. Each insert operation must wait for a corresponding remove operation by another thread, and vice versa. With this very unusual collection there are no elements stored (just a piece of info is transferred from one thread to another, one at a time). Bounded: not applicable |
put, offer, take, poll, : O(1) Other methods has no effect as there's no formal collection data structure so poll() always returns null, isEmpty() always returns false, size() always return 0, iterator() always returns empty iterator etc. |
LinkedTransferQueue |
TransferQueue |
Linked data structure. Unbounded Similar handoff concept as SynchronousQueue (above) but is a formal collection and has extra interface level support methods like tryTransfer, transfer etc. A useful transfer scenario: if the queue has elements in it already and transfer/tryTransfer called, it waits for the queue to empty including the new transferred element. |
offer, peek, poll:O(1) size: O(n) All blocking methods decrease throughput. Generally LinkedTransferQueue outperforms SynchronousQueue by many factors. |
LinkedBlockingDeque |
BlockingDeque |
Doubly linked nodes. Optionally-bounded. Appropriate choice over LinkedBlockingDeque when many threads access the collection. |
remove, removeFirstOccurrence, removeLastOccurrence, contains, iterator.remove() and the bulk operations: O(n) Other operations: O(1)(ignoring time spent blocking). |
ConcurrentLinkedDeque |
Deque |
Doubly linked nodes. Unbounded. Non-blocking but still thread-safe. Doesn't permit null. Appropriate choice over LinkedBlockingDeque when many threads access the collection. |
Index based get and remove methods: O(n) size: O(n). Not reliable because of asynchronous operations All other methods: O(1) Iterators are weakly consistent They don't throw ConcurrentModificationException and don't show the changes consistently to other threads. |
CopyOnWriteArrayList |
List |
Arrays of Objects . Thread-safe. All mutative methods (add, set, and so on) are implemented by making a fresh copy of the underlying array. |
add, contains, remove(E e): O(n) get(Int index), next: O(1) Lock is only used for write methods to increase throughput for reads. Write operation is costly because copying a new arrays every time. Not recommended if there are a lot of random writes. Iterators are very fast (no locks and thread interference). They never throw ConcurrentModificationException, neither are consistent with the writes made after the creation of iterators. Element-changing operations on iterators (remove, set, and add) are not supported. These methods throw UnsupportedOperationException. |
CopyOnWriteArraySet |
Set |
Arrays of Objects(Internally uses CopyOnWriteArrayList instance, avoiding duplicate elements by using CopyOnWriteArrayList.addIfAbsent). Thread-safe. |
add, contains, remove(E e): O(n) next: O(1) Iterator behavior is same as CopyOnWriteArrayList. |
ConcurrentSkipListSet |
NavigableSet |
Backed by ConcurrentSkipListMap (Skip list data structure), where elements are populated as keys of the map. Thread-safe. Insertion, removal, and access operations safely execute concurrently by multiple threads. |
contains, add, and remove operations and their variants: O(log n) next: O(1) size: O(n). Not reliable because of asynchronous operations. Iterators and spliterators are weakly consistent. |