Question

In the new Java memory model, any write to a variable is guaranteed to be completed before the next thread read it.

I wonder if this is also the case for variables that are member of this object.

for java memory model:

http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html

E.g.

public class VolatileTest {
    private volatile Map<String, Function> functionMap = new HashMap<>();

    public Function getFunction(String key) {
        Function function = this.functionMap.get(key);

        if (function == null) {
            //Is this guaranteed to be fully constructed? I don't think so.
            function = new Function(key);

            this.functionMap.put(key, function);
        }

        return function;
    }
}

Like above code, even by making functionMap volatile, it still doesn't guarantee the function object will be fully constructed before this method return.

Is my thinking right?

Also just for this topic, I'd like you guys to check is my thinking right for the following:

Like the below any write to the functionMap is guaranteed to be completed before changing the reference of functionMap, right? No matter how long the initializeMap methods take, the other thread will either see a null functionMap or a fully initialized functionMap?

public Map<String,Function> getFunctionMap (){
    Map<String, Function> tempMap = new HashMap<>();

    initalizeMap(tempMap); //fill in values

    // Above operation is guaranteed to be completed
    //   before changing the reference of this variable.
    this.functionMap = tempMap;

    // So here you will either see a null or a fully initialized map.
    // Is my understanding right?
    return this.functionMap;
}

Just to clarify above, above two examples are all in multi-thread environment and functionMap variables will be accessed by multiple thread.

Was it helpful?

Solution

This is exactly when ConcurrentHashMap should be used

private final ConcurrentMap<String, Function> functionMap = new ConcurrentHashMap<>();

public Function getFunction(String key) {
        Function function = functionMap.get(key);
        if (function == null) {
            function = new Function(key);
            Function oldFunction = functionMap.putIfAbscent(function);
            if (oldFunction != null) {
                 function = oldFunction;
            }
        }
        return function;
}

OTHER TIPS

@grumpynerd Hi, Your first assumption is right about new Function(key) may be partially constructed after return of the method except if it is an immutable Object.

Your second assumption is wrong.

 this.functionMap = tempMap;

    // So here you will either see a null or a fully initialized map.
    // Is my understanding right?
    return this.functionMap;

now here you are writing and reading a same volatile variable next to next. And you assume that initializeMap will be completed before returns statement. Your assumption hole True in Roach Motel Model. But its not necessarily true in JMM. as JMM is weaker model than Roach. See answer to the below question Is this a better version of Double Check Locking without volatile and synchronization overhead

Updated from here as it cannot come in comment. Am sorry your second assumption also hold true. But keep in mind this.functionMap = tempMap; may in practice behaves like all the statement have been executed before it and been flush to the memory as it is explained by Doug lea cookbook http://g.oswego.edu/dl/jmm/cookbook.html this, but this is only a guide on how to implement JMM. For example look the code here Is this a better version of Double Check Locking without volatile and synchronization overhead

here i tried to use volatile fence as an memory barrier even if you make the statement

fence = true; 
fence = fence;

Now here i am doing volatile read and write, assuming that this will make my object fully initialized before returning the reference and hence if i again check that reference to be non-null then i am sure that the object will be fully initialized. But the above reasoning is flawed because am trying to think in terms of memory barriers rather than the java happen-before guarantees. What is flawed in my question was that field reference was not volatile therefore any other thread is not reading any volatile reference. If its found field !- null even then it may read wrong values for the other instance fields of FieldType. As there is no guarantees by java when reading non-volatile values.

Therefore never thinks in terms of restricting ordering of statements. Always think what is guaranteed by JLS/JMM happens before relationship.

for example

int count = 1; volatile boolean flag = true;

now jls state the above code will run in program order. i.e count will be 1 first and then flag = true will be executed.

    But Not the catch above from jls http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html

        It should be noted that the presence of a happens-before relationship 
between two actions does not necessarily imply that they 
have to take place in that order in an implementation. 
If the reordering produces results consistent with a legal execution, 
it is not illegal.

        For example, the write of a default value to every 
field of an object constructed by a thread need not 
happen before the beginning of that thread, as long as no 
read ever observes that fact.

The above states that count = 1 may be executed after flag = true. if the execution is a legal execution. But our observations would be like that count=1 has been executed first. keyword is observation. (It may happen here also if suppose compiler finds that flag not shared among other threads then it might be legal .. this is just hyposthesis)

but now if in another thread executes the below statement then

    if(flag == true) {
       sysout(count) // count is  guaranteed  to be 1 
       // because of volatile guarantees flag will be only true if it has been set by first thread
       // and by program order count must have been set before flag has been set to  true.
       // therefore by program order i am printing count after reading value of a 
//volatile variable flag, therefore count must be 1.
    }

now if flag would have not been volatile then there is no happen-before relationship between the two threads. thread 2 very well could read flag == true and count as 0 as there are no observational guarantees among threads.

In short guarantees are for observations and not how actually each statement executes. read more about Program Order, Synchronization Order and Happens Before Order and lets share the knowledge :) http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.3

Marking a variable volatile is useful only if the variable value change and is accessed by more than one thread. Your functionMap is a reference to the map and unless your want to assign a new Map instance to this reference, volatile here is useless. To be clear, volatile will not affect the Map content, only the Map pointer.

I feel that you are think that Java method calls are asynchronous, it is not. In your first example new Function(key); call will not return until the Function object is fully initialized.

Same thing for the second, unless that initalizeMap starts a new thread to do it's stuff, there is no problem here. And even in the case that intializeMap makes some asynchronous work, as you have new HashMap<>(), tempMap cannot be null, may be not fully loaded with the values added by initalizeMap (I repeat only if initalizeMap starts a new Thread) but no null.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top