Java Concurrency--DCL problem

Source: Internet
Author: User

Transferred from: http://www.iteye.com/topic/875420

If you search the reason why the DCL in Java fails in the online analysis, will talk about the compiler will do optimization and so on, I believe you see this must feel very frustrated, very helpless, the process of their own writing is not confident. I understand this feeling because I've been there, and that's probably why people on the Internet have always liked to talk about DCL. If placed before Java5, from the compiler's point of view to explain the DCL is understandable, in the java5 of the JMM (memory model) has been greatly modified, if now only from the compiler's point of view to explain the DCL, it is simply insulting Java, The biggest advantage of Java is that it only needs to consider a platform. You can completely ignore the majority of the online discussion of the DCL, many times they themselves are not clear, in addition to Doug Lea and several other Daniel, I do not believe who is more authoritative than who.  

Many people do not understand the DCL, not the complexity of the DCL, but rather the lack of mastery of the foundation. So, I'll start with the basics and then analyze the DCL.  

We all know that data contention occurs when two threads read (or write) a shared variable at the same time. How am I supposed to know that data competition is happening? I need to read that variable, the data competition usually has two performance: one is to read the stale data, that is, read the data that has been written, but not the latest. The second is to read the value that was not written before, that is to say garbage.  

Data stale  

In order to read the latest data written to another thread, JMM defines a series of rules, the most basic of which is to take advantage of synchronization. In Java, the means of synchronization are synchronized and volatile two kinds, here I will only involve syncrhonized. Please remember the following rules, and then I will speak carefully.  

Rule one: You must synchronize all writes and all reads of a variable to read the latest data.  

Look at the following code first:  

public class A {private int some;public int another;public int getsome () {return some;} public synchronized int Getsomewithsync () {return some;} public void Setsome (int v) {some = V;} Public synchronized void Setsomewithsync (int v) {some = V;}}

Let's analyze one thread to write and another to read in four scenarios. The initial situation is a = new A (), and no other threads are considered.

Scenario One: Read and write are not synchronized.

Thread1 Thread2
(1) a.setsome (13)
(2) A.getsome ()

In this case, even if Thread1 writes some for 13,thread2 and then reads some, can it read to 13? In the absence of synchronized coordination, the results are indeterminate. As you can see from the diagram, two threads run independently, and JMM does not guarantee that one thread will see the value written by another thread. In this case, Thread2 might read 0 (that is, some's initial value) instead of 13. Note that in theory, even if Thread2 waits 10,000 years after Thread1 writes some, it is still possible to read some's initial value of 0, although this is almost impossible in practice.

Scenario Two: Write synchronization, read different steps

Thread1 Thread2
(1) A.setsomewithsync (13)
(2) A.getsome ()



Scenario Three: Read sync, write out of sync

Thread1 Thread2
(1) a.setsome (13)
(2) A.getsomewithsync ()





In both cases, thread1 and thread2 only have a lock on the read or write some, which has no effect, and as in [case one], thread2 is still likely to read the initial value of some 0. As can be seen from the diagram, Thread1 and thread2 do not have any effect on each other, one lock does not affect the other party's continued operation. The figure also shows that the synchronization operation is equivalent to starting the lock operation at synchronization and performing the unlock operation at the end of the synchronization.

Scenario Four: Read and write are synchronized

Thread1 Thread2
(1) A.setsomewithsync (13)
(2) A.getsomewithsync ()





In scenario four, when Thread1 writes some, thread2 waits for the thread1 write to complete, and it can see the thread1 changes to some, at which point the thread2 is guaranteed to read to 13. In fact, Thread2 can see not only the thread1 changes to some, but also any modifications that Thread1 made prior to modifying some. To be more precise, the lock operation of one thread can see all the changes before the unlock operation of the same object on the other, notice the red arrow in the figure. Follow the arrows in the graph to indicate the direction, and the arrows at the end always see the changes made at the beginning of the arrows. In this way, a.some[thread2] can see lock[thread2],lock[thread2] can see UNLOCK[THREAD1],UNLOCK[THREAD1] and can see A.SOME=13[THREAD1], You can see that the value of some is 13.

Let's look at a slightly more complicated example:

Example Five

Thread1 Thread2
(1) A.another = 5
(2) A.setsomewithsync (13)
(3) A.getsomewithsync ()
(4) A.another = 7
(5) A.another





What value does thread2 eventually read about another? Will read the initial value of another 0, after all, all access to another is not synchronized? No. It is clear that Thread2 's another at least 5 of the thread1 written before lock does not guarantee that it will see the thread1 in unlock write 7. Therefore, Thread2 can read what the value of another may be 5 or 7, but it will not be 0. You may have found that if you remove the thread2 read a.some in the diagram, then it is equivalent to an empty synchronization block, which has no effect on the conclusion. This means that an empty synchronization block is working, and the compiler cannot arbitrarily optimize the empty synchronization block, but you should be particularly careful with empty synchronization blocks, which are usually not the result you want. It is also important to note that the unlock operation and the lock operation must be for the same object to ensure that the unlock operation can see the changes made before the lock operation.

example six: different locks

Class B {private Object Lock1 = new Object ();p rivate Object lock2 = new Object ();p rivate int some;public int getsome () {sy Nchronized (LOCK1) {return some;}} public void Setsome (int v) {synchronized (Lock2) {some = V;}}}

Thread1 Thread2
(1) b.setsome (13)
(2) B.getsome ()





In this case, although both Getsome and Setsome are locked, because they are different locks, one thread runs without blocking another thread from running. So the situation here and the situation one or two, three, THREAD2 does not guarantee read to Thread1 write some up-to-date value.

Now look at the DCL:

example seven: DCL

public class Lazysingleton {private int somefield;        private static Lazysingleton instance;                                 Private Lazysingleton () {This.somefield = 201;                               (1)} public static Lazysingleton getinstance () {if (instance = = null) {                       (2) synchronized (Lazysingleton.class) {//(3) if (instance = = null) {           (4) Instance = new Lazysingleton ();                                      (5)}}} return instance;                                (6)} public int Getsomefield () {return this.somefield; (7)}} 

Suppose Thread1 calls getinstance () first, and since no thread has created a Lazysingleton instance at this time, it creates an instance of S and returns. This is thread2 again called getinstance (), when it runs to (2), because at this point the read instance is not synchronized, it is possible to read S or null (refer to scenario two). First consider the case where it reads S, and draw a flowchart like this:



Since Thread2 has read S, getinstance () immediately returns S, which is no problem, but the problem occurs when it reads s.somefiled. It can be seen that thread2 does not have any synchronization, so it may not see the value of Thread1 writing Somefield 20, and for thread2 it may read S.somefield to 0, which is the underlying problem of the DCL. As can be seen from the above analysis, it is almost always impossible to try to fix the DCL but want to avoid synchronization altogether.

Next consider Thread2 in (2) to read the case of instance null, draw a flowchart:



Next thread2 will read the value of the instance in the case of a lock, when it is guaranteed to read S, the reason for reference to the case four or by the arrows in the diagram to indicate direction to determine.

About the DCL says so much, leaving two questions:

      1. Then consider thread2 in (2) Read the case of instance null, and what will it call s.somefiled to get? Will you get 0?
      2. Why is the DCL double check, can I remove the check at (4)? If not, why?

Atomic Nature
Back to the situation one, why do we say thread2 read that the value of some can only be 0 or 13, but not for others? This is determined by the atomicity of the Java to int, the reference read and write. The so-called "Atomic", is an inseparable unit of the smallest, the concept of database transactions should be easy to understand this. When Some=13 is called, either the write succeeds or the write fails, and half of the write is not possible. However, Java's read and write to double, long is not atomic, which means some extreme surprises may occur. See Example:

public class C {private * * volatile */long x;                           (1) public void SetX (Long v) {x = V;} Public long GetX () {return x;}}

  

Thread1 Thread2
(1) c.setx (0x1122334400112233l)
(2) C.getx ()



The value of thread2 reading X may be 0,1122334400112233, and may be a completely unexpected value for other things. One scenario assumes that the JVM's write to Long is a write-down of 4 bytes, and then 4 bytes higher, then the value read to X may also be 112233. However, we do not assume that the JVM does so, in order to ensure that reading or writing to a long or double is atomic, there are two ways, one is to use volatile, and the other is to use synchronized. For the above example, if you cancel the volatile comment at (1), you will be able to ensure that the value of Thread2 read to X is either 0 or 1122334400112233. If you use synchronization, you must synchronize the Getx,setx as follows:

public class C {private * * volatile */long x;                           (1) Public synchronized void SetX (Long v) {x = V;} Public synchronized Long GetX () {return x;}}

So there are rules for atomicity (volatile is also a kind of synchronization).

rule two: for double, long variable, only for all read and write synchronization, can guarantee its atomicity

Sometimes we need to guarantee the atomicity of a compound operation, and then we can only use synchronized.

public class Canvas {private int CurX, cury;public/* synchronized */GetPos () {return new int[] {CurX, curY};} Public/* synchronized */void MoveTo (int x, int y) {CurX = X;cury = y;}}

  

Thread1 Thread2
(1) C.moveto (1, 1)
(2) C.moveto (2, 2)
(3) C.getpos ()



When there is no synchronization, Thread2 's GetPos may get [1, 2], although that point may never have occurred. The result is that when Thread2 calls GetPos (), CurX has 0, 1, or 23 possibilities, the same Cury also has 0, 1 or 23 possible, so GetPos () may return [0,0], [0,1], [0,2], [1,0], [ ], [to], [2,0], [2,1], [2,2] A total of nine possible. To avoid this situation, only GetPos () and MoveTo are set as synchronous methods.

Summarize
The above analysis of the two symptoms of data competition, stale data and non-atomic operations, are due to lack of proper synchronization caused. These are actually quite basic knowledge, synchronization can have two effects: one is to ensure that the latest data, the second is to ensure the operation of the atom, but most of the books are too much emphasis on the latter, the former lack of understanding, so that there are many misunderstandings on the understanding of multithreading. If you want to master Java threading Advanced Knowledge, I only recommend Java Concurrent programming design principles and patterns. In fact, I have not written Java for a long time, these things are my knowledge two years ago, if there are problems, you are welcome to point out, please do not be polite.

There is also a story that is very good: http://www.iteye.com/topic/260515

  

Java Concurrency--DCL problem

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.