3. JDK Concurrent Container
- Concurrent collection
Concurrent HashMap: This is an efficient concurrent HashMap. You can understand it as a thread-safe HashMap.
CopyOnWriteArrayList: This is a List, which is a family of ArrayLists by name. In the case of reading more and writing less, this List performs very well, far better than Vector.
Concurrent LinkedQueue: An efficient concurrent queue, implemented with linked lists. Think of it as a thread-safe LinkedList.
BlockingQueue: This is an interface that JDK implements internally through linked lists, arrays, etc. Represents a blocked queue that is well suited for data sharing channels.
Concurrent SkipListMap: Implementation of jump tables. This is a Map, which uses the data structure of the jump table for fast lookup. - Thread-safe HashMap
The Collections class can be used to convert a normal HashMap to a thread-safe map
Collections.synchronizedMap(new HashMap())
private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable { private static final long serialVersionUID = 1978198479659022715L; private final Map<K,V> m; // Input map final Object mutex; // Lock the resource object, which will be locked by any operation on the map SynchronizedMap(Map<K,V> m) { this.m = Objects.requireNonNull(m); mutex = this; } SynchronizedMap(Map<K,V> m, Object mutex) { this.m = m; this.mutex = mutex; } public int size() { synchronized (mutex) {return m.size();} } public boolean isEmpty() { synchronized (mutex) {return m.isEmpty();} } public boolean containsKey(Object key) { synchronized (mutex) {return m.containsKey(key);} } public boolean containsValue(Object value) { synchronized (mutex) {return m.containsValue(value);} } public V get(Object key) { synchronized (mutex) {return m.get(key);} } public V put(K key, V value) { synchronized (mutex) {return m.put(key, value);} } public V remove(Object key) { synchronized (mutex) {return m.remove(key);} } public void putAll(Map<? extends K, ? extends V> map) { synchronized (mutex) {m.putAll(map);} } public void clear() { synchronized (mutex) {m.clear();} } ....... //ellipsis }
- List thread security
Collections.synchronizedList(new LinkedList<String>())
- Concurrent LinkedQueue Class for Efficient Read-Write Queue
The best performance queues in high concurrent environments are mainly non-blocking queues and lock-free operations using CAS.
First, let's look at its Node node:
private static class Node<E> { volatile E item; //Current object volatile Node<E> next; //Next object to build the list Node(E item) { UNSAFE.putObject(this, itemOffset, item); } boolean casItem(E cmp, E val) { //(Expected value, set target value), cas operation return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); } void lazySetNext(Node<E> val) { UNSAFE.putOrderedObject(this, nextOffset, val); } boolean casNext(Node<E> cmp, Node<E> val) { return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } private static final sun.misc.Unsafe UNSAFE; private static final long itemOffset; private static final long nextOffset; static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> k = Node.class; itemOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("item")); nextOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("next")); } catch (Exception e) { throw new Error(e); } } }
tail pointer updates within the ConcurrentLinkedQueue class are not real-time, and there may be delays. Each update jumps two elements, as shown below:
Then look at the new node offer() method:
public boolean offer(E e) { checkNotNull(e); //Non-empty check final Node<E> newNode = new Node<E>(e); for (Node<E> t = tail, p = t;;) { //for loop has no exit and knows the setup is successful Node<E> q = p.next; //Get the next object of tail node if (q == null) { //For the first insert, the p.next object is empty // p is the last node if (p.casNext(null, newNode)) { //Insert a new element, p=t //Update tail twice if (p != t) casTail(t, newNode); return true; } // cas Competition Fails, Cycle Again } else if (p == q) //Meeting Sentinels // We have fallen off list. If tail is unchanged, it // will also be off-list, in which case we need to // jump to head, from which all live nodes are always // reachable. Else the new tail is a better bet. p = (t != (t = tail)) ? t : head; else // Check for tail updates after two hops. p = (p != t && t != (t = tail)) ? t : q; //t!=(t=tail)!= It's not an atomic operation. First, take the value of T on the left and then t=tail on the right. } }
- Efficient reading: CopyOnWriteArrayList class in invariant mode
Use scenario: Read operation is much larger than write operation. The faster the read operation is, the better. It's OK to write slowly.
Features: read without lock, write will not block the read operation, only write and write need to wait synchronously, read performance greatly improved
Principle: Write in a self-replication, modify the content to write in the copy, after writing, then replace the original data with the copy content.
public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); //Duplicate newElements[len] = e; //New arrays instead of old ones setArray(newElements); return true; } finally { lock.unlock(); } }