0

Discussing this answer I was wondered why didn't we use sychronization when assigning default values.

class StateHolder {
    private int counter = 100;
    private boolean isActive = false;

    public synchronized void resetCounter() {
            counter = 0;
            isActive = true;
    }

    public synchronized void printStateWithLock() {
        System.out.println("Counter : " + counter);
        System.out.println("IsActive : " + isActive);
    }

    public void printStateWithNoLock() {
        System.out.println("Counter : " + counter);
        System.out.println("IsActive : " + isActive);
    }
}

This class looks thread safe because access to its fields is managed by a synchronized method. That way, all we have to do is to publish it safely. For instance:

public final StateHolder stateHolder = new StateHolder();

Can it be considered as a safe publication? I think no, it cannot. Consulting the final field semantic (emphasized mine) I figured out that the only thing guarnteeed is that stateHolder reference is not a stale one. :

A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

final field semantic is not concerned about the state of the ojbect referenced to by the final field. That way, another thread might as well see default values of the fields.

QUESTION: How can we guarantee memory consistency of the filed's values assigned within a constructor or instance initializer?

I think we have to declare them either volatile or final as that there is no happens-before relationship bethween assigning a reference and constructor invocation. But lots of library classes does not declare fields that way. java.lang.String is an example:

public final class String 
          implements java.io.Serializable, Comparable<String>, CharSequence{
    //...
    private int hash; //neither final nor volatile
    //...
}
Community
  • 1
  • 1
St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • The reason string doesn't declare it `volatile` is because repeated calculations of the value only wastes CPU cycles, but the return result will always be the same number – Ferrybig Apr 06 '16 at 08:18
  • "[`String.hash` is] neither `final` nor `volatile`" because it doesn't rely upon the field being set exactly once or more than once, respectively. String evaluates its hash code lazily - most of the time it will only calculate it at most once - the first time you call `hashCode()` - but if it has to evaluate the hash code more than once, meh, rare occurrence. – Andy Turner Apr 06 '16 at 08:30
  • @Ferrybig So to provide memory consistency we should declare fields final or volatile even when we use safe-publication mechanism? – St.Antario Apr 06 '16 at 08:40

1 Answers1

3

final can guarantee you that you'll see the assigned value to an instance variable after the instance construction without the need of any further action. You just need to make sure that you don't leak the constructed instance within the constructor.

volatile can also guarantee you that you'll see the default value that you set for some instance variable, because the instance variable initializers are guaranteed to be executed before the end of the constructor per JLS 12.5 Creation of New Class Instances.

Safe publication is not entirely trivial, but if you stick with one of the popular mechanisms for achieving it, you should be perfectly fine. You can take a look at Safe Publication and Safe Initialization in Java for some more interesting details.

As for String.hash, it's a popular example of the so-called benign data races. The accesses to the hash instance variable allow both racing of a read and write and racing of two writes. To illustrate just the latter, two threads can simultaneously:

  • see the initial value of 0
  • decide that they are the first to calculate the hash
  • calculate the hash code and write to the same variable without any synchronization

The race was still allowed and considered to be benign because of two reasons:

  1. Hash code calculation for the immutable String instances is an idempotent operation.
  2. Writes of 32-bit values are guaranteed not to tear.

Even benign data races are still not recommended though. See Benign data races: what could possibly go wrong? or Nondeterminism is unavoidable, but data races are pure evil.

Dimitar Dimitrov
  • 16,032
  • 5
  • 53
  • 55
  • But what about the `StateHolder` example? How can we make sure that any thread will see fresh values of its field? – St.Antario Apr 06 '16 at 09:32
  • 1
    I think in your specific case, it makes sense to make the fields `volatile` if you cannot provide their initializing value by construction time, or `final` if you can. – Dimitar Dimitrov Apr 06 '16 at 10:00
  • I think I'm coming to understand the concept. BTW, would you mind looking at more specific [question](http://stackoverflow.com/questions/36447710/how-to-publish-stringbuffer-safely) about publishing `StringBuffer`? – St.Antario Apr 06 '16 at 10:02