12

Small question about memory visibility.

CodeSample1:

class CustomLock {

    private boolean locked = false;

    public boolean lock() {
        if(!locked) {
            locked = true;
            return true;
        }
        return false;
    }
}

This code is prone to bugs in a multi-threaded environment, first because of the "if-then-act" which is not atomic, and second because of potential memory visibility issues where for example threadA sets the field to true, but threadB that later wishes to read the field's value might not see that, and still see the value false.

The simplest solution is to use the synchronized keyword, as in CodeSample2.

CodeSample2:

class CustomLock {

    private boolean locked = false;

    public synchronized boolean lock() {
        if(!locked) {
            locked = true;
            return true;
        }
        return false;
    }
}

Now what if I wish to use an atomic variable, and for the example, an AtomicBoolean (question applies to all atomic variables),

CodeSample3:

   public static class CustomLock {
    private AtomicBoolean locked = new AtomicBoolean(false);

    public boolean lock() {
        return locked.compareAndSet(false, true);
    }
}

Better performance considerations aside, we can see that now we've implemented similar logic to the "if-then-act" from CodeSample1, using AtomicBoolean. It doesn't really matter what the code does logically, the question I have is what if 2 threads invoke the lock() method in CodeSample3 right about the same time, while it's clear that any write operation to the field will now be done atomically, does the use of AtomicBoolean also guarantees memory visibility?

Sorry for the long story, just wanted to make sure I'm coming across as clear as possible, Thanks guys...

JamesJenkins
  • 131
  • 4

2 Answers2

13

Yes, according to the javadocs it guarantees:

compareAndSet and all other read-and-update operations such as getAndIncrement have the memory effects of both reading and writing volatile variables.

Gray
  • 115,027
  • 24
  • 293
  • 354
Iłya Bursov
  • 23,342
  • 4
  • 33
  • 57
  • @JamesJenkins it's usually, darned near always actually, useful to read the documentation when you have a question. – Lew Bloch Mar 17 '17 at 23:12
3

the question I have is what if 2 threads invoke the lock() method in CodeSample3 right about the same time, while it's clear that any write operation to the field will now be done atomically, does the use of AtomicBoolean also guarantees memory visibility?

For AtomicBoolean to handle multiple operations from different threads at the same time it has to guarantee memory visibility. It can make the guarantee because it wraps a volatile field. It is the language semantics of the volatile which ensures that memory barriers are crossed so that multiple threads see the most up to date value and that any updates will be published to main memory.

Btw, your lock(...) method should ready be tryLock(...) because it might not get the lock.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • whatever it wraps or not are implementation details, the documentation is the place to look at. you can have a `synchronized` method that *is* atomic, but if the shared data that is updated inside it is exposed without synchronizing on the same lock, there is no visibility guarantee – Eugene Sep 30 '18 at 19:27
  • I think I've commented on the wrong answer :| sorry – Eugene Oct 15 '18 at 17:43