Question

Suppose I have a class which wraps a HashMap as follows:

public final class MyClass{

     private final Map<String, String> map;

     //Called by Thread1
     public MyClass( int size ){
         this.map = new HashMap<String, String>( size );
     }

     //Only ever called by Thread2
     public final String put( String key, String val ){
        return map.put( key, value );
     }

     //Only ever called by Thread2
     public final String get( String key ){
        return map.get( key );
     }

     //Only ever called by Thread2
     public final void printMap(  ){
       //Format and print the contents of the map
     }

}

This class is initialized via "Thread1". However, the put, get, printMap and other operations are only ever called by "Thread2".

Am I correct in understanding that this class is thread safe as:

  1. Since the reference to the map is declared final, all other threads would see the initial state of the map (happens-before is established).

  2. Since put/get/printMap/etc are only ever called by Thread2, there is no need for mutual-exclusion.

Thank you

Was it helpful?

Solution

So what you are asking is correct assumption. You do not need to make it thread safe if you can guarantee that it is only used this way. You can't pass a half constructed object around in java, so the "Constructor might not be finished" is not possible.

So if you do

new Thread(new Runnable() {

    public void run() {
          final MyClass instance = new MyClass(10);
          new Thread(new Runnable() {
               public void run() {
                    instance.put("one", "one");
                      ....
               }

           }).start();
     }

}).start();

You are fine :) This is what you described, created by Thread1 but only used by Thread2. There is no way for the thread to collide with itself.

Thread-Safe is a different definition where the composed entity may safely be interacted with by multiple threads. In the case you described, that scenario does not happen, since you essentially have a thread that constructs and another that manipulates.

OTHER TIPS

This is a bit tricky to answer, because the JLS doesn't contain the concept of a class being thread-safe — all it specifies is relationship between actions (e.g. a write to a field, a read from a field, etc).

That said, by most definitions, this class is not thread-safe -- it contains data races caused by unsynchronized access to a not-thread-safe Map. However, your usage of it is thread-safe, because you safely publish this.map to Thread 2 after construction, and at that point this.map is only ever accessed by one thread, in which case thread safety isn't an issue.

In other words, this is only slightly more involved than asking whether HashMap is thread-safe when created and accessed only within one thread. The answer in that case is that HashMap is not thread safe, but it doesn't need to be.

Similarly, your class isn't thread safe, but it sounds like it may not need to be.

If you stand to your definition of what will happen then it would in fact be thread safe. That is, only thread-2 will put and get from the Map.

Since the Map is declared final, you do establish a happens-before relationship with respect to the write of thread-1 and read of thread-2.

a) Since the reference to the map is declared final, all other threads would see the initial state of the map (happens-before is established).

Yes. An empty HashMap with size specified.

b) Since put/get/printMap/etc are only ever called by Thread2, there is no need for mutual-exclusion.

Yes as well, though this type of logic usually scares me in practice :)

The question here is not about the class implementation itself, but about if the manipulation of the class you describe is thread-safe or not, so the class implementation doesn't really matter.

I can think of 2 possible ways of Thread2 accessing the MyClass instance.

  1. Both Thread1 and Thread2 are run independently. In this case it's not thread-safe because Thread2 can call put/get/printMap/etc before the constructor running in Thread1 has finished, resulting in a NPE. The MyClass instance accessed by Thread2 can be null depending on how Thread2 accesses it. If it's a shared instance, when Thread2 accesses it, it can be null if MyClass constructor has not finished executing in Thread1.
  2. Thread1 creates the instance and passes this instance to Thread2. In this case it is thread-safe, but this is actually irrelevant because there's no multi-thread access to this instance because Thread2 has to wait for the instance to be passed.

So in the situation you describe, the answer actually depends not on the class implementation but in how its instance is shared. And in worst-case scenario (way 1) it is not thread-safe.

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