问题
I know that the JVM memory model is made for lowest common denominator of CPUs, so it has to assume the weakest possible model of a cpu on which the JVM can run (eg ARM).
Now, considering that x64 has a fairly strong memory model, what synchronization practices can I ignore assuming I know my program will only run on 64bit x86 CPUs? Also does this apply when my program is being run through virtualization?
Example:
It is known that JVM's memory model requires synchronizing read/writes access to longs and doubles but one can assume that read/writes of other 32 bit primitives like int, float etc are atomic.
However, if i know that I am running on a 64 bit x86 machine, can i ignore using locks on longs/doubles knowing that the cpu will atomically read/write 64 bit values and just keep them volatile (like i would with ints/floats)?
回答1:
I know that the JVM memory model is made for lowest common denominator of CPUs, so it has to assume the weakest possible model of a cpu on which the JVM can run (eg ARM).
That's not correct. The JMM resulted from a compromise among a variety of competing forces: the desire for a weaker memory model so that programs can go faster on hardware that have weak memory models; the desire of compiler writers who want certain optimizations to be allowed; and the desire for the result of parallel Java programs to be correct and predictable, and if possible(!) understandable to Java programmers. See Sarita Adve's CACM article for a general overview of memory model issues.
Considering that x64 has a fairly strong memory model, what synchronization practices can I ignore assuming I know my program will only run on [x64] CPUs?
None. The issue is that the memory model applies not only to the underlying hardware, but it also applies to the JVM that's executing your program, and mostly in practice, the JVM's JIT compiler. The compiler might decide to apply certain optimizations that are allowed within the memory model, but if your program is making unwarranted assumptions about the memory behavior based on the underlying hardware, your program will break.
You asked about x64 and atomic 64-bit writes. It may be that no word tearing will ever occur on an x64 machine. I doubt that any JIT compiler would tear a 64-bit value into 32-bit writes as an optimization, but you never know. However, it seems unlikely that you could use this feature to avoid synchronization or volatile fields in your program. Without these, writes to these variables might never become visible to other threads, or they could arbitrarily be re-ordered with respect to other writes, possibly leading to bugs in your program.
My advice is first to apply synchronization properly to get your program correct. You might be pleasantly surprised. The synchronization operations have been heavily optimized and can be very fast in the common case. If you find there are bottlenecks, consider using optimizations like lock splitting, the use of volatiles, or converting to non-blocking algorithms.
UPDATE
The OP has updated the question to be a bit more specific about using volatile
instead of locks and synchronization.
It turns out that volatile
not only has memory visibility semantics. It also makes long
and double
access atomic, which is not the case for non-volatile
variables of those types. See the JLS section 17.7. You should be able to rely on volatile
to provide atomicity on any hardware, not just x64.
While I'm at it, for additional information about the Java Memory Model, see Aleksey Shipilev's JMM Pragmatics talk transcript. (Aleksey is also the JMH guy.) There's lots of detail in this talk, and some interesting exercises to test one's understanding. One overall takeaway of the talk is that it's often a mistake to rely on one's intuition about how the memory model works, e.g. in terms of cache lines or write buffers. The JMM is a formalism about memory operations and various contraints (synchronizes-with, happens-before, etc.) that determine ordering of those operations. This can have quite counterintuitive results. It's unwise to try to outsmart the JMM by thinking about specific hardware properties. It'll come back to bite you.
回答2:
you would still need to handle thread-safety, so volatility semantics and memory fences will still matter
What I mean here is, eg in Oracle Java, most low-level sync operations end up in Unsafe (docjar.com/docs/api/sun/misc/Unsafe.html#getUnsafe), which in turn has a long list of native methods. So in the end, those synchronization practices and lots of other low-level operations are encapsuled by the JVM where they belong. x64 has not the same jvm as x86.
after reading your edited question again: the atomicity of your load/store operations was a topic here. So no, you don't have to worry about atomic 64bit load/stores on x64. But since this is not the end of all sync issues, see the other answers.
回答3:
Always include the memory barriers where the JVM memory model states that they are needed and then let the JVM optimize them when it can for different platforms.
Knowing that you run only on x86 CPUs does not mean that you can drop using memory barriers. Unless perhaps you know that you will only run on single core x86 cpus ;) Which, in todays multi core world no body really knows.
Why? Because the java memory model has two main concerns.
- visibility of data between cores and
- happens before guarantees, aka re-ordering.
Without a memory barrier in play, the order of operations that become visible to other cores could become very confusing; and that is even with the stronger guarantees offered by x86. x86 only ensures consistency once the data makes it to the cpu caches, and while its ordering guarantees are very strong they only kick in once Hotspot has told the CPU to write out to the cache.
Without the volatile/synchronized then it will be up to the compilers (javac and hotspot) as to when they will do those writes and in what order. It is perfectly valid for them to decide to keep data for extended periods within the registers. Once a volatile or synchronized memory barrier is crossed, then the JVM knows to tell the CPU to send the data out to the cache.
As Doug Lea documents in the JSR-133 Cookbook most of the x86 barriers are reduced to no-op instructions that guarantee the ordering. Thus the JVM will make the instructions as efficient as possible for us. Code to the Java Memory Model, and let Hotspot work its magic. If Hotspot can prove that synchronised is not required, it can drop it entirely.
Lastly, the double checked locking pattern was proven to be broken on multi core x86 too; despite its stronger memory guarantees. Some nice detail of this was writen by Bartos Milewski on his C++ blog and again this time specific to Java here
回答4:
Compiler Writes Have taken care of What you wanted to do. Many of the volatile read/write barriers will eventually be no-op on x64. Also do think reordering may also be induced because of compiler optimization and may not depend on Hardware. For exmple benign data races - for example String hashCode. See : http://jeremymanson.blogspot.com/2008/12/benign-data-races-in-java.html
Also Please refer the page for what instructions may be no-op on x64. See : http://gee.cs.oswego.edu/dl/jmm/cookbook.html see Multiprocessors Section.
I will advise not to do any optimizations specific for hardware. You may end up writing Unmaintainable Code. Compiler Writers Have already put up sufficient HardWork.
回答5:
It not only depends on CPU, but also on the JVM, operating system etc.
One thing you might be sure: don't assume anything if it comes to thread synchronization.
来源:https://stackoverflow.com/questions/24918252/what-jvm-synchronization-practices-can-i-ignore-assuming-i-know-i-will-run-on-x6