2

So Im experimenting a bit with multithreading currently, since im still pretty new to Java. Now, I have multiple threads that all influence the same long variable. However, it seems that afer a while of not doing anything but checking the if statement the while loop just stops executing (as in, it loops infinitely). It does work if i just print something within the while-loop.

Does not work:

while(true){
  if(longVariable < 2)
    break;
}

Does somehow work:

while(true){
  System.out.println("hi");
  if(longVariable < 2)
    break;
}

Why is this?

LucaW
  • 79
  • 1
  • 6

2 Answers2

4
while(true){
  if(longVariable < 2)
    break;
}

In this code, there is no reason for the JVM to believe that longVariable will ever change. It can effectively rewrite it to:

long cached = longVariable;
while(true){
  if(cached < 2)
    break;
}

Which loops infinitely if longVariable is at least two when it executes, because nothing can change cached.

You have to give the compiler a hint that this rewrite isn't allowed. It works with the System.out.println on your JVM because it happens to be implemented with synchronization (which is common, but not required). This inserts memory barriers which means that the cached value of longVariable is invalidated, and has to be read again.

But it's not guaranteed to work. To make it work correctly, either declare the variable volatile:

volatile long longVariable

which prevents its value from being cached.

Or use something like an AtomicLong instead of a plain long variable.

Or, most onerously, use explicit synchronization (ensuring that all reads of and writes to the variable are synchronized on the same "something"):

long v;
synchronized (something) {
  v = longVariable;
}
if (v < 2) ...
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
0

When you have a non volatile variable which is not updated by a thread, it is free to inline it.

In your first case, once the code has compiled by the JIT, it might no longer read the value and instead make the condition always true.

In the second case, you have a thread safe operation. println on System.out is a synchronized method. This adds a read and write barrier and prevents the JIT from optimising the read operation away.

If you try this, it should also work.

while(true){
  synchronized("hi") { } // does nothing but add memory barriers.
  if(longVariable < 2)
    break;
}

It also slows down the code by more than 1000x so the method might not have been JITed by the time you try to stop the thread.

The simple solution is to make the variable volatile and it will be read in a thread safe manner every time.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130