简体   繁体   中英

Java forcing volatile access

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 .

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.

如果您将HashMap声明并初始化为静态字段,它将以线程安全的方式由Java类加载器初始化。

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.

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.

Cf 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.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM