Is the cause of JIT this behavior?

Inspired by this question , I wrote a test:

public class Main { private static final long TEST_NUMBERS = 5L; private static final long ITERATION_NUMBER = 100000L; private static long value; public static void main(final String [] args) throws Throwable { for(int i=0; i<TEST_NUMBERS; i++) { value = 0; final Thread incrementor = new Thread(new Incrementor()); final Thread checker = new Thread(new Checker()); incrementer.start(); checker.start(); checker.join(); incrementer.join(); } } static class Incrementor implements Runnable { public void run() { for(int i=0; i<ITERATION_NUMBER; i++){ ++value; } } } static class Checker implements Runnable { public void run() { long nonEqualsCount = 0; for(int i=0; i<ITERATION_NUMBER; i++){ if(value != value) { ++nonEqualsCount; } } System.out.println("nonEqualsCount = " + nonEqualsCount); } } } 

This program prints in the general case:

 nonEqualsCount = 12; //or other non 0 value; nonEqualsCount = 0; nonEqualsCount = 0; nonEqualsCount = 0; nonEqualsCount = 0; 

First: I will explain that this behavior is the presence of a JIT compiler. The cache value of the JIT compiler is not volatile for each thread after the warm-up. It is right?

Second: If right or wrong at first, how can I confirm this?

PS - I know about PrintAssebly -option.

Update: environment: Windows 7 64bit, JDK 1.7.0_40-b43 (Hot Spot).

+7
java multithreading jit
source share
4 answers

What you see is probably a JIT artifact. Before it starts to use, Java bytecode is interpreted, which means that there are many chances that the verification thread will be interrupted during the comparison.

In addition, since more code is being executed, it is likely that CPU caching will require cleaning.

When the code is optimized by JIT, it is likely to introduce 64-bit operations, and since only a small amount of code is executed, the caches will no longer be red in main memory, which means that threads are not able to see changes made by others.

+2
source share

The incremental variable long not atomic (64-bit). In the state (value != value) : it may happen that between the read value , the first thread may change the value. The volatile type is associated with visibility . Values โ€‹โ€‹of non-volatile variables may be deprecated. So your first conclusion seems correct.

+3
source share

On the first pass of your program, these statements may be correct:

This code can demonstrate that the increment operation ( ++value ) for a variable of type long (as well as int ) is not atomic. In addition, it can also demonstrate that the != Operation != not thread safe unless used in a synchronized block. But this has nothing to do with the data type used.

Your observation that something has changed after the first pass is also correct, but, for example, if you use the Oracle / SUN JVM, the implementation of this JIT-Complier ("Hotspot Engine") depends on the technical architecture starts.

So it's hard to say and make sure that the JIT compiler is responsible for this. An attempt to derive implementation details of the JIT-Complier / Hotspot mechanism using this approach is a fairly empirical research method. Perhaps your observation may change when switching from Solaris to Windows.

Here is a link to implementation details of the Hotspot engine: http://www.oracle.com/technetwork/java/javase/tech/index-jsp-136373.html

To get additional empirical results, you can, for example, try to return the JVM to run in classic mode or reduce the number of optimizations in the JVM (client mode?). If the behavior changes, this may be another indicator of the correctness of your theory.

Anyway: I'm curious that your results are nonetheless :-)

+2
source share

As long as you are right that this is caused by JIT, it has nothing to do with volatile.

Some JITs do internal optimizations on the fly and remove unnecessary code to speed things up, and this is exactly what happens here. JIT determines that comparing value != value always falsely and completely removes the entire block of code. In addition, it can determine that this for loop is now running empty and also removes the entire loop. As a result, this will be the final optimized verification class:

 public void run() { System.out.println("nonEqualsCount = 0"); } 

You can verify this by measuring the time it takes to complete this thread for each pass. On the first pass, it may take some time to finish, on the second it will be only a few nanoseconds for println.

Note. Typically, you cannot expect JIT to do anything. Based on actual implementation, hardware, and other factors, this may or may not optimize your code. And if it is optimized, the result is also impossible to determine, since, for example, the code can be optimized much earlier on slow equipment than on fast hardware.

+2
source share

All Articles