0

In some article I read that double check locking is broken. As the compiler can reorder the sequence of constructors.

  1. Ss allocate memory for an object
  2. Then return the address to a reference variable
  3. Then initialize the state of the object

While typically one would expect:

  1. It should be as allocated memory for the object
  2. then initialize the state of object
  3. then return the address to the reference variable.

Again, when using the synchronized keyword, the code reorder never happens as per JMM specification.

Why is compiler reordering the sequence of constructor events when it is inside the synchronized() block?

I saw a lot of posts here about DCL, but I am expecting the description based on JMM and compiler reordering.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user2392631
  • 497
  • 8
  • 15
  • 2
    It would be helpful if you posted the specific double-check lock idiom you are thinking of in code form. There are a few different variations of DCL. – Tim Bender Aug 06 '13 at 03:09

3 Answers3

3

The compiler is free to reorder instructions within a synchronized block. And the compiler is free to reorder instructions before (as long as they stay before) or after (as long as they stay after) the synchronized block. However, the compiler is not free to reorder instructions across the synchronized block boundaries (block start or block end).

Thus, the construction and assignment which are wholly within the synchronized block can be reordered, and an outside viewer which has not correctly synchronized can see the assignment before the construction.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jtahlborn
  • 52,909
  • 5
  • 76
  • 118
  • 4
    In addition to instruction reordering, the other classic problem with DCL (which happens in Java as well as other languages) is in multiprocessor systems, caching can cause changes done by one processor to be perceived as out of order on another. See http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html for a good full description of DCL issues in Java. – Jason C Aug 06 '13 at 03:47
0

Why is compiler reordering the sequence of constructor events when it is inside the synchronized() block?

It would typically do this to make the code run faster.

The Java Language Specification (JLS) says that the implementation (for example, the compiler) is allowed to reorder instructions and sequences of instructions subject to certain constraints.

The problem is that the broken variants of DCL make assumptions that fall outside of what the JLS says can be made. The result is an execution that the JLS says is not well-formed. Whether this manifests itself as an actual bug / unexpected behaviour depends on the compiler version, the hardware and various other things.

But the point is that the compiler isn't doing anything wrong. The fault is in the DCL code.


I just want to add that the JIT compiler is often not reordering the events per se. what it is often doing is removing constraints on hardware-level memory read/write actions. For example, by removing the constraint that a particular memory write is flushed to main memory, you allow the hardware to defer (or even skip entirely) a slow write-to-memory, and just write to the L1 cache. By contrast, the end of a synchronized block will force the cached writes to main memory, incurring extra memory traffic and (probably) a pipeline stalls.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
0

First of all:

Again when using the synchronized keyword, the code reorder never happens as per the JMM specification.

The above statement is not fully accurate. The JMM defined the happens-before relationship. The JLS only defines the program order and happens-before order. See 17.4.5. Happens-before Order.

It has effects on the reordering of instructions. For example,

int x = 1;
synch(obj) {
    y = 2;
}
int z = 3;

Now for the above piece of code, the below types of reordering are possible.

synch(obj) {
    int x = 1;
    y = 2;
    int z = 3;
}

The above is a valid reordering.

See Roach Motels and The Java Memory Model.

synch(obj) {
    int z = 3;
    y = 2;
    int x = 1;
}

The above is also a valid reordering.

What is not possible is that y=2 will only be executed after the lock has been acquired and before the lock is released this is what guaranteed given by JMM. Also to see the correct effects from another thread, we need to access y inside the synchronized block only.

Now I come to DCL.

See the code of DCL.

if (singleton == null)
    synch(obj) {
        if(singleton == null) {
            singleton == new Singleton()
        }
    }
return singleton;

Now the problem in the above approach is:

  1. singleton = new Singleton() is not a single instruction. But a set of instructions. It is quite possible that a singleton reference is assigned an object reference first, before fully initializing the constructor.

  2. So if 1 happens then it's quite possible the other thread reads a singleton reference as a non null and thus is seeing a partially constructed object.

The above effects can be controlled by making a singleton as volatile which also establishes happens-before guarantees and visibility.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
veritas
  • 2,444
  • 1
  • 21
  • 30