HashMap is not thread-safe and often requires some way to avoid writing programs. In fact, JDK native provides 2 ways to make HASHMAP support thread safety.
Method One: Return a new map via Collections.synchronizedmap (), the new map is thread-safe. This requires that you are accustomed to programming based on interfaces, because the return is not a hashmap, but a map implementation.
Method Two: Rewrite the hashmap, the specific can see JAVA.UTIL.CONCURRENT.CONCURRENTHASHMAP. This method has a great improvement over the method one.
The following 2 implementation methods are analyzed and compared from all angles.
- Implementation principle
- The different locking mechanisms
- How to get/release a lock
- Advantages and Disadvantages
1) Principle of implementation
Method One principle:
by Collections.synchronizedmap () to encapsulate all unsafe hashmap methods, even the ToString, hashcode are encapsulated. The key points of the package are 2, 1) using the classic synchronized for mutual exclusion, 2) a new class using the proxy mode, this class also implements the map interface.
private Static Class Synchronizedmap<k,v>
Implements Map<k,v>, Serializable {
Use Serialversionuid from JDK 1.2.2 for interoperability
Private static final long serialversionuid = 1978198479659022715L;
Private final map<k,v> m; backing Map
Final object mutex;//object on which to synchronize
Synchronizedmap (map<k,v> m) {
if (m==null)
throw new NullPointerException ();
THIS.M = m;
Mutex = this;
}
Synchronizedmap (map<k,v> m, Object mutex) {
THIS.M = m;
This.mutex = Mutex;
}
public int size () {
Synchronized (mutex) {return m.size ();}
}
//***********************************
Save space and remove a lot of similar code
//***********************************
Public String toString () {
Synchronized (mutex) {return m.tostring ();}
}
private void WriteObject (ObjectOutputStream s) throws IOException {
Synchronized (mutex) {s.defaultwriteobject ();}
}
}
Method Two principles:
Re-write the HashMap, the larger changes have the following points.
A new locking mechanism (which can be understood as optimistic locking) is used later in detail
The HashMap is split and split into separate blocks, which reduces the likelihood of lock collisions in case of high concurrency
Public V put (K key, V value) {
if (value = = null)
throw new NullPointerException ();
int hash = hash (Key.hashcode ());
return Segmentfor (hash). Put (key, hash, value, false);
}
2) Different locking mechanisms
Method one is used by the Synchronized method, which is a pessimistic lock. Before entering, you need to obtain a lock, make sure to enjoy the current object, and then make the corresponding modification/reading.
Method two uses an optimistic lock, and the comparison and previous values are modified if the object needs to be modified, and if it is modified by another thread, the failure is returned. The implementation of the lock, using the Nonfairsync. This feature ensures that the modified atomicity, mutex, cannot be resolved at the JDK level, where the JDK calls the Jni method, and JNI invokes the CAS directive to ensure atomicity and mutex. Readers can learn more about their own Google JAVA CAs. How the optimistic lock in Java is implemented.
When more than one thread is concurrenthashmap to the same segment, then a single thread gets run, and the other threads are Locksupport.park (), and after the execution is done, Automatically picks a thread to execute Locksupport.unpark ().
V Put (K key, int hash, V value, Boolean onlyifabsent) {
Lock ();
try {
int c = count;
if (c + + > Threshold)//ensure capacity
Rehash ();
hashentry<k,v>[] tab = table;
int index = hash & (tab.length-1);
Hashentry<k,v> first = Tab[index];
hashentry<k,v> e = first;
while (E! = null && (E.hash! = Hash | |!key.equals (E.KEY)))
e = E.next;
V OldValue;
if (E! = null) {
OldValue = E.value;
if (!onlyifabsent)
E.value = value;
}
else {
OldValue = null;
++modcount;
Tab[index] = new hashentry<k,v> (key, hash, first, value);
Count = C; Write-volatile
}
return oldValue;
} finally {
Unlock ();
}
}
3) How to get/release the lock
Get Lock:
Method One: Above the HashMap, synchronized locks the object (not the class), so the first request to get the lock, the other threads will go into the block, waiting to wake up.
Method Two: Check the abstractqueuedsynchronizer.state, if 0, then get the lock, or the applicant has been locked, you can also quit the lock, and state also added 1.
Release Lock:
is to get the reverse operation of the lock, and the use of the correct, two methods are automatically selected a queue of threads to obtain the lock can get CPU resources.
4) Advantages and disadvantages
Method One:
Advantages: Code implementation is very simple, one can understand.
Cons: From a lock point of view, method one directly uses the lock method, which basically locks the largest possible block of code. Performance will be poor.
Method Two:
Pros: There are fewer pieces of code that require mutual exclusion, and performance is better. Concurrenthashmap the whole map into a number of blocks, the probability of a lock collision is greatly reduced, the performance will be better.
Cons: The code implementation is slightly more complex.
(reproduced) Two ways to make HashMap thread safe