Synchronize write access to Volatile field (Cheap

2019-02-12 23:35发布

问题:

Let's say I have the following class that will be read heavily, but only written to occasionally. It will be used in a multi-threaded web app, so it needs to be thread safe:

public class Foo {
    private volatile String foo;
    public String getFoo() {
        return foo;
    }
    public synchronized String setFoo(String in) {
        this.foo = in;
    }
}

Java Concurrency (http://www.ibm.com/developerworks/java/library/j-jtp06197/index.html) states that this is a fragile way to protect write access while improving read access. What is a stronger alternative to this pattern? Or any alternative if foo will need to mutable in a read-heavy environment? Thank you.

回答1:

Volatile provides fast thread-safe lock-free access to a field without synchronization

private volatile String foo;

public String getFoo() {
    return foo;
}
public void setFoo(String in) {
    this.foo = in;
}

volatile solves 3 problems 1) memory visibility 2) atomic writes for double and long fields 3) forbids instructions reordering. But it's not enough if you need several operations over a field as one atomic transaction, such as increment. This code is broken

private volatile int id;

public void incrementId() {
     id++;
}

because if 2 threads simulataneously read and increment it and save the result then the result of the first increment will be overwritten with the result of the second increment. To prevent this from happening we need to use synchronization

 private int id;

 public synchronized int nextId() {
       return ++id;
 }

or java.util.concurrent.atomic package

 private AtomicInteger id = new AtomicInteger();

 public void incrementId() {
     return id.incrementAndGet();
 }


回答2:

If all you are doing is setting foo, then you don't need to synchronize the method. making the reference volatile is sufficient.



回答3:

At the link you said there is this code for "infrequent updates" usage:

@ThreadSafe
public class CheesyCounter {
    // Employs the cheap read-write lock trick
    // All mutative operations MUST be done with the 'this' lock held
    @GuardedBy("this") private volatile int value;

    public int getValue() { return value; }

    public synchronized int increment() {
        return value++;
    }
}

The increment method is only using synchronized because it is doing more than just setting the value of value as stated in the description, if all you are doing is this.foo = in; that is atomic. In the text the "fragility of this pattern" means things can get messy very fast when you mix volatile and other synchronization methods to do more than just simple examples. See package java.util.concurrent.locks for the interfaces Condition and Lock and the class ReentrantLock. I think that, and using synchronized is what the author means by "stronger alternatives". You should also see Object.wait, Object.notify and Object.notifyAll if you don't know that yet.