The problem
Listing 1
class SingleInstance{
private static single = null;
private SingeInstance(){}
public SingleInstance getSingleInstance() {if(single == null)//0
synchroznied(SingleInstance.class){
if(single == null){ single = new SingleInstance(); / / 1}}returnsingle; }}Copy the code
The problem with the code above is that orderliness is not guaranteed at 1, i.e., the code is actually divided into two large steps
- Initialize SingleInstance
- Assign this object to the variable single
There is uncertainty before and after this step. As soon as the thread hits 1, it may assign the object to Single first but it hasn’t initialized yet. When thread 2 runs at 0, it finds that this condition is not met and returns single. While single is a non-empty reference, it is not a correct object. This is where double check can go wrong.
volatile
You’ve probably heard that volatile single fixes this problem after JDK1.4, but do you know why it fixes it?
The semantics of volatile preserve order and visibility, but not atomicity
visibility
What is visibility?
In order to
count = 0;
couont++;
Copy the code
As an example of an error, this line of code is executed as follows:
- Load the value of count from memory into its own thread stack
- Incrementing count in its own thread stack
- Put the modified value back into main memory.
In the case of multiple threads
- Thread 1 performs the first operation
- Thread 2 then performs the first operation
- Thread 1 performs the next two operations, at which point count in main memory becomes 1.
- Thread 2 goes on to do the second operation, which is a copy of its stack that’s 0 and increments by 1, and then does the third operation to write the 1 back into main memory.
See the problem? I added it twice and it’s still 1. Something’s wrong, man!
What role does volatile play in this?
When a thread performs an operation such as ****2 above after volatile count, it does not value in the copy, but in main memory.
And even that doesn’t solve the counting problem. Why?
Atomicity is not guaranteed,i++ operation is non-atomic operation
- Thread 1 increments the value from memory, and the thread copy count becomes 1.
- And then thread 2 takes a value from main memory, it gets a value of 0, it increments by 1, it writes to main memory, and count becomes 1 in main memory.
- Thread 1 performs step 3 to write the count of its copy back to main storage, which is still 1.
summary
The visibility semantics of volatile are intended to ensure that the thread takes the latest value from main memory (step 2 above) rather than its own copy, but atomicity is not guaranteed.
order
In the Java memory model, the compiler and processor are allowed to reorder instructions, but the reordering process does not affect the execution of a single-threaded program, but affects the correctness of multithreaded concurrent execution.
Example: Listing 2
Thread A context = initContext(); //1 flag =true; //2 thread Bwhile(! flag){ sleep(100); } dosomething(context);Copy the code
The code is fine in A single thread, but as shown in Listing 2, the code in thread A might be reordered by running code 2 and then code 1 and that would be A problem.
Using volatile prevents reordering, and the language level of reading and writing data is atomic.
atomic
Atomicity is simply indivisibility, so if it’s an atomic operation, it must either be done or not done at all, it can’t be done partially.
conclusion
Volatile fields guarantee visibility and prohibit reordering, but they do not provide atomicity. The reason is that under the condition of multi-threading, the execution order cannot be guaranteed, and there will be thread switching in the middle.
Going back to Listing 1 remember the original question? What’s wrong with this singleton in Listing 1 we’ve already talked about. How do you solve it? This is easily solved by using the volatile keyword, and by adding volatile to the single variable. The reason is that volatile has the ability to disable reordering. So the object is initialized and then assigned to the variable, and the single detected at zero is returned correctly instead of being an incomplete single.