3

Consider a situation like this.

There are two threads and a shared resource(like a HashMap). One thread created the HashMap and initialized it with some key-value pairs and after the shared resource is initialized it will never be modified again.

Now, the second thread is created strictly after the shared resource is initialized and wants to use that resource. At this point I would like some guarantee that the second thread will use the correct version of the shared resource. I presume it is possible that the first thread didn't flush the changes to the main memory before the second thread is created so the second thread will take the old value of the shared resource to it's cache.

Is this analysis correct, and how to force flush to main memory in Java by hand after initializing the shared resource as in this particular situation where I do not want or require volatile or synchronized.

Marin Veršić
  • 404
  • 4
  • 10
  • @Robert that's not true at all. – JB Nizet Jan 15 '18 at 19:27
  • 1
    I'm somewhat confused about what you're asking for. If you want to make something synchronized with out using 'synchronized' use a ConcurrentHashmap. And if you want to make sure that one thread works first then the other works you can set up a reenterant lock and have the second thread wait until the hashmap is freed. Or you can kick off one thread with another thread to absolutly guarantee the second thread will use the correct version. – Richard Jan 15 '18 at 19:29

4 Answers4

4

The documentation says:

A call to start on a thread happens-before any action in the started thread.

So, if your code matches your description, it's safe.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • …assuming that “*second thread is created strictly after*” truly implies an ordering according to the Java Memory Model, e.g. if the first thread calls `start()` after the map is fully initialized. To name a counter-example, relying on `Thread.sleep` to assume an order by timing would be incorrect. – Holger Jan 19 '18 at 07:49
1

If you declare and initialize your HashMap as static field it will be initialized by Java class loader in a thread safe fashion.

Andrei Amarfii
  • 685
  • 1
  • 6
  • 17
1

If map initialisation happens before start of the second thread then everything is correct. To simplify analysis and to make things simple you can convert ininitialized map into some immutable map implementation and pass it to the created thread explicitly. And this way you would not need to use a shared variable at all.

Serge Bogatyrev
  • 818
  • 4
  • 5
1

Is this analysis correct, and how to force flush to main memory in Java by hand after initializing the shared resource as in this particular situation where I do not want or require volatile or synchronized.

It's not possible to not require volatile or synchronized. You have to use some form memory synchronization between threads or stuff doesn't work.

You could use a static initializer as Andrei mentioned (*), or final, both of which imply a memory barrier. But you have to use something.

You may need to require a synchronized map (Collections.synchronizedMap()) or a CurrentHashMap, but you still need to use volatile, synchronized, final or static to guard the field itself.

C.f. Java Concurrency in Practice by Brian Geotz, and also this related question on Stack Overflow (note that the OP gets the name of the book wrong).

(* The whole static initializer thing is kinda complicated, and you should read Mr. Goetz's book, but I'll try to describe it briefly: static fields are part of class initialization. Each static field or static initializer block is written, or executed, by a thread (which could be the thread that called new or accessed the class object for the first time, or could be a different thread). When the process of writing all static fields for the first time is done, the JVM inserts a memory barrier so that the class object, with all its static fields, is visible to all threads in the system as required by the spec.

You do NOT get a memory barrier per field write, like volatile. The class load tries to be efficient and only inserts one barrier at the very end of initialization. Thus you should only use static initializers for what they're supposed to be for: filling in fields for the first time, and don't try to write entire programs inside a static initializer block. It's not efficient and your options for thread safety are actually more limited.

However, the memory barrier that's part of class initialization is available to use, and that's why, Andrei Amarfii said, the pattern of using a static initialzer in Java is used to ensure visibility of objects. It's important enough that Brian Goetz calls it out as one of his four "Safe Publication" patterns.)

markspace
  • 10,621
  • 3
  • 25
  • 39
  • @Holger Yes it does. Please read Java Concurrency in Practice. Brian Goetz explains how it works. Basically, `static` means the field is initialized by the class loader, and at the end of the class load there **must** be a memory barrier because any thread in the system could access the class object, and the JVM must ensure all initial writes to it are visible. Also read the question on Safe Publication I linked to; static initialization is explicitly mentioned. – markspace Jan 19 '18 at 21:33
  • 1
    *If* you are initializing a `static` field in the class initializer, you get a memory barrier, but that's a property of the initializer, not because the field is `static`, as the fact that a field is `static` does not imply that it will be initialized in the class initializer or that it will never get modified afterwards. The way you have written it, readers will assume that modifying a `static` field at any time was safe, which it isn't. As far as I remember, all examples of the book used `static final` which enforces correct usage, but does no match your “`final` ***or*** `static`”. – Holger Jan 20 '18 at 14:10
  • I'll update my answer to reflect that the memory barrier is only available for static initializer, that's fair. For the rest, you do get a memory barrier on all static fields (initially), the only way to initialize them is during class load, even if it's just the default value and there's no explicit initializer. 'final` produces a memory barrier even on instance fields (that's how you get immutable objects) so the 'or' there is quite correct. Either works, both is just a common idiom. – markspace Jan 20 '18 at 16:22