javamultithreadingconcurrencythread-safetysafe-publication

Giving recovery time for an unsafely published java.lang.String


java.lang.String is only effectively immutable. Brian Goetz of "Java Concurrency in Practice" said something like effectively immutable objects will only be thread safe if safely published. Now, say I unsafely publish String like this:

public class MultiThreadingClass {
    private String myPath ="c:\\somepath"; 
    //beginmt runs simultaneously on a single instance of MultiThreading class
    public void beginmt(){
        Holder h = new Holder();
        h.setPath(new File(myPath)); //line 6
        h.begin();
    }
}

public class Holder {
    private File path;
    public void setPath(File path){
        this.path = path;
    }
    public void begin(){
        System.out.println(path.getCanonicalPath()+"some string");
    }
}

At the moment that the MultiThreadingClass is initializing with its constructor, it could happen that the File constructor on line 6 may not see the value of myPath.

Then, about three seconds after the construction of the unsafely published String object, threads on MultiThreadingClass are still running. Could there still be a chance that the File constructor may not see the value of myPath?


Solution

  • Your statement that you are asking your question about:

    At the moment that the MultiThreadingClass is initializing with its constructor, it could happen that the File constructor on line 6 may not see the value of myPath.

    The answer is complicated. You don't need to worry about the char-array value inside the String object. As I mentioned in the comments, because it is a final field that is assigned in the constructors, and because String doesn't pass a reference to itself before assigning the final field, it is always safely published. You don't need to worry about the hash and hash32 fields either. They're not safely published, but they can only have the value 0 or the valid hash code. If they're still 0, the method String.hashCode will recalculate the value - it only leads to other threads recalculating the hashCode when this was already done earlier in a different thread.

    The reference myPath in MultiThreadingClass is not safely published, because it is not final. "At the moment that the MultiThreadingClass is initializing with its constructor", but also later, after the constructor completed, other Threads than the thread that ran the constructor may see the value null in myPath rather than a reference to your string.

    There's an example in the Java Memory Model section of the Java Language Specification [version 8 linked but this is unchanged since JMM was released in JSR-133]:

    Example 17.5-1. final Fields In The Java Memory Model

    The program below illustrates how final fields compare to normal fields.

    class FinalFieldExample { 
        final int x; 
        int y; 
    
        static FinalFieldExample f; 
    
        public FinalFieldExample() { 
            x = 3; 
            y = 4; 
        } 
    
        static void writer() { 
            f = new FinalFieldExample(); 
        } 
    
        static void reader() { 
            if (f != null) { 
                int i = f.x; // guaranteed to see 3 
                int j = f.y; // could see 0 
            } 
        } 
    }
    

    The class FinalFieldExample has a final int field x and a non-final int field y. One thread might execute the method writer and another might execute the method reader.

    Because the writer method writes f after the object's constructor finishes, the reader method will be guaranteed to see the properly initialized value for f.x: it will read the value 3. However, f.y is not final; the reader method is therefore not guaranteed to see the value 4 for it.

    This is even likely to happen on a heavily loaded machine with many threads.

    Workarounds/solutions: