The discussion of this issue comes from an internal discussion about the "security problem of using hashmap in multi-threaded environments". The problem of hashmap multithreading has been raised once before. See the previous blog. This articleArticleThis article mainly discusses race condition under multiple threads. The following content references an internal Email:
ErrorCode:
Define member variables
Private Static Map cachedmap = New Hashmap (7000 );
Private Static Boolean firstinvoke = true;
Program It is the first time that the map variable is initialized.
Thread 1:
Public object getmyvalue (){
If (firstinvoke ){
While (I <7000 ){
............
Cachedmap. Put ( "New" , "Newvalue" );
I ++;
}
Firstinvoke = false;
}
}
Thread 2:
When thread 1 is put to the cachedmap object, thread 2 takes the value from this cachedmap
Cachedmap. Get ( "New" );
Error Analysis
Single-step debug is okay, but thread security occurs when the code is working in the case of multiple threads. Hashmap is not secure for read and write threads. Only all read-only resources are secure for threads. When hashmap is used for concurrent read/write operations, thread security issues may occur, generally, data errors are caused by thread security issues. The hashmap program may be suspended when multiple threads perform simultaneous read/write operations.
Reference from http://sdh5724.javaeye.com/blog/619130 below
Analysis: we know that when hashmap is used for concurrent read/write, it will throw the concurrentmodificationexception exception, but the JDK documentation clearly states that this exception throw is a design method of fail-fast, the purpose is to enable developers to realize thread security issues as early as possible. However, this fail-fast does not necessarily happen, but may happen. Therefore, in an uncertain state, it is easier to understand the JVM thread's sustained 100% CPU behavior (for (Entry <K, V> E = table [I]; e! = NULL; E = E. Next). Currently, it can only be estimated that the Code enters the endless loop state, but it cannot be very clear ).
"Correct usage"
Check whether concurrent thread reading exists when changing the content of hashmap. If yes, synchronize the Read and Write entries. if you know that you want to read and write map in multi-thread mode, we recommend that you use the thread-safe concurrenthashmap instead of hashmap. Concurrenthashmap can provide good concurrency without compromising thread security.
The Code is as follows:
Private Static Map cachemap = New Concurrenthashmap (7000 );
Private Static Boolean firstinvoke = true;
The program initializes the map variable for the first time.
Thread 1:
Public object getmyvalue (){
If (firstinvoke ){
While (I <7000 ){
............
Cachedmap. Put ( "New" , "Newvalue" );
I ++;
}
Firstinvoke = false;
}
}
Thread 2:
When thread 1 is put to the cachedmap object, thread 2 takes the value from this cachedmap
Cachedmap. Get ("New" );
Race Condition of the above SolutionProblem:
This problem of improper use of hashmap is classic. Most of the time, we use the "single thread" mindset to write code. Without knowing it, we forget the multi-thread scenario at runtime.
In fact, I think there is an implicit race condition problem in the following example, that is, in the logic of IF (firstinvoke) then load data and firstinvoke = false.
That is, if (firstinvoke ){...// Timeout this may cause multiple threads to enter at the same time, resulting in multiple load data
Generally, we use a Boolean variable to implement the lazy operation. In a multi-threaded environment, remember to use the synchronize keyword or volatile type variable + CAS operation, make sure that the variables are correctly read and written by each thread.
1. Insurance practices(In the latest JVM, this method is the safest, most readable, and most cost-effective. If JVM supports lock escapeBiased lockingAnd the performance will be very good)
Synchronized (LOCK ){
If (firstinvoke ){
Then load data...
Firstinvoke = false
}
}
2. Or, Using the volatile variable + DCL
PrivateVolatile BooleanFirstinvoke = true;
If (firstinvoke ){
Synchronized (LOCK ){
If (firstinvoke ){
Then load data...
Firstinvoke = false;
}
}
}
3.SMPFriendly but lazyWith atomicboolean, compareandset operation is used. (Volatile only ensures the visibility of variables, and the spinning CAS ensures the atomicity of operations)
Private atomicboolean firstinvoke =NewAtomicboolean (true );
If (firstinvoke. getandset (false )){// CAS spinning inside the atomicboolean: getandset () method
Then load data...
}
4. Finally, the most complex, but at the same time meet the SMP-friendly, and the best performance:
PrivateAtomicboolean firstinvoke =NewAtomicboolean (true );
For(;;){
Boolean current = firstinvoke. Get ();
If (! Current ){// The most likely condition branch, see http://pt.alibaba-inc.com/wp/dev_related/optimization_363/likely-unlikely.html
Break;
}
If (firstinvoke. compareandset (current, false ){
Then load data...
Break;
}
}
In the xxx code, to ensure optimal performance in SMP state, we also use the CAS + spinning technique in some key areas.
We may not always use the "four back-to-word writing methods", but it is necessary to understand the basic concepts of JVM memory visibility and operation atomicity, this is also a prerequisite for ensuring that thread-safe code is written ).
References:
Http://sdh5724.javaeye.com/blog/619130
Http://www.tech-faq.com/race-condition.html
The art of Multiprocessor programming http://book.douban.com/subject/3024605/
Race Condition by @ Shawn
Related information:
Lightweight locking)
Implementation principle of Java biased locking)
In-depth understanding of the security of DCL (dual-check lock)