Home > Back-end >  Do I need to use volatile?
Do I need to use volatile?

Time:09-20

Consider the following code:

public class MyDataStructure {

    int size;
    final ReentrantLock lock = new ReentrantLock();

    public void update() {
        lock.lock();
        try {
            // do coll stuff
            size  ;
        } finally {
            lock.unlock();
        }
    }

    public int size() {
        return size;
    }
}

As far as I understand, ReentrantLock imposes an happens-before relationship so size needs to affect the main memory and the cache (of other threads) with the new value. Hence, no need to use volatile for size. Also, for the same reason the method size() can simply return size.

Is my understanding correct?

CodePudding user response:

No.

Other threads may see a stale (old) value of size.

When other threads execute size() there is nothing there instructing it to ensure it's not reading an old value, such as a locally cached value in another cpu cache etc. Also worth mentioning is hoisting by the jvm.

AN example, if you have a loop calling size() in one thread, it may never exit if update() is not called in the loop, or size changed, and called/changed from other threads only.

while (size() == 0) {
   ...
}

The jit compiler, (at least the modern part what used to be called the c2/server compiler) could happily optimize away (hoist) the size variable checks as it sees that it never changes in the loop.

CodePudding user response:

As far as I understand, ReentrantLock imposes an happens-before relationship...

Yes. When some thread W (W for "writer") unlocks a lock, that "happens before" some other thread R (for "reader") subsequently locks the same lock.

In order to take advantage of that relationship, you have to lock the same lock in both methods:

public class MyDataStructure {

    int size;
    final ReentrantLock lock = new ReentrantLock();

    public void update() {
        lock.lock();
        try {
            size  ;         // happens before unlock() because things that happen
                            //  in one thread all happen in _program order._
        } finally {
            lock.unlock();  // Happens before a pending lock.lock() call in some
                            //  other thread.
        }
    }

    public int size() {
        lock.lock();        // Happens before return size because...
                            //  ...program order.
        try {
            return size;
        } finally {
            lock.unlock();
        }
    }
}
  • Related