Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

291
Views
Java multithreading - joining a CPU heavy thread and volatile keyword

So, follwoing some job interviews I wanted to write a small program to check that i++ really is non-atomic in java, and that one should, in practice,add some locking to protect it. Turns out you should, but this is not the question here.

So I wrote this program here just to check it.

The thing is, it hangs. It seems that the main thread is stuck on on t1.join() line, even though both worker threads should finish because of the stop = true from previous line.

I found that the hanging stops if :

  • I add some printing inside the worker threads (as in the comments), probably causing the worker threads to sometime give up CPU or
  • If I mark the flag boolean stop as volatile, causing the write to immediately be seen by worker threads, or
  • If I mark the counter tas volatile... for this I have no idea what causes the un-hanging.

Can someone explain what's going on? why do I see the hang and why does it stop in those three cases?

public class Test {   

    static /* volatile */ long t = 0;
    static long[] counters = new long[2]; 
    static /* volatile */ boolean stop = false;

    static Object o = new Object();
    public static void main(String[] args) 
    {
        Thread t1 = createThread(0);
        Thread t2 = createThread(1);

        t1.start();
        t2.start();

        Thread.sleep(1000);

        stop = true;

        t1.join();
        t2.join();

        System.out.println("counter : " + t + " counters : " + counters[0] + ", " + counters[1]  + " (sum  : " + (counters[0] + counters[1]) + ")");

    }

    private static Thread createThread(final int i)
    {
        Thread thread = new Thread() { 
            public void run() {
                while (!stop)
                {
//                  synchronized (o) {                      
                        t++;
//                  }

//                  if (counters[i] % 1000000 == 0)
//                  {
//                      System.out.println(i + ")" + counters[i]); 
//                  }
                    counters[i]++;
                }
            }; 
        };
        return thread;
    }
}
about 4 years ago · Santiago Trujillo
3 answers
Answer question

0

It seems that the main thread is stuck on on t1.join() line, even though both worker threads should finish because of the stop = true from previous line.

In the absence of volatile, locking, or other safe publication mechanism, the JVM has no obligation to ever make stop = true visible to other threads. Specifically applied to your case, while your main thread sleeps for one second, the JIT compiler optimizes your while (!stop) hot loop into the equivalent of

if (!stop) {
    while (true) {
        ...
    }
}

This particular optimization is known as "hoisting" of the read action out of the loop.

I found that the hanging stops if :

  • I add some printing inside the worker threads (as in the comments), probably causing the worker threads to sometime give up CPU

No, it's because PrintStream::println is a synchronized method. All known JVMs will emit a memory fence at the CPU level to ensure the semantics of an "acquire" action (in this case, lock acquisition), and this will force a reload of the stop variable. This is not required by specification, just an implementation choice.

  • If I mark the flag boolean stop as volatile, causing the write to immediately be seen by worker threads

The specification actually has no wall clock-time requirements on when a volatile write must become visible to other threads, but in practice it is understood that it must become visible "very soon". So this change is the correct way to ensure that the write to stop is safely published to, and subsequently observed by, other threads reading it.

  • If I mark the counter t as volatile... for this I have no idea what causes the un-hanging.

These are again the indirect effects of what the JVM does to ensure the semantics of a volatile read, which is another kind of a "acquire" inter-thread action.

In summary, except for the change making stop a volatile variable, your program switches from hanging forever to completing due to the accidental side-effects of the underlying JVM implementation, which for simplicity does some more flushing/invalidation of thread-local state than required by the specification.

about 4 years ago · Santiago Trujillo Report

0

Those might be the possible reasons :

  • Marking "stop" as volatile prevents its value being cached in the worker threads states (e.g registers)
  • Marking "t" as volatile makes sure that updated value is read when "t" is accessed. However, this behavior might also get "stop"'s updated value if JVM arranged those variables near each other, so they might be read and stored in the same "cache line". Try adding @Contended annotation to see if behavior persists in this case as well .You may find additional information on : https://en.wikipedia.org/wiki/False_sharing , https://mechanical-sympathy.blogspot.am/2011/07/false-sharing.html , https://blogs.oracle.com/dave/entry/java_contented_annotation_to_help
  • Calling the "System.out.println()" actually performs a system call hence making a transition to the "native calls stack" which probably also makes the "processor cache" to purge. What does a JVM have to do when calling a native method?

If you are interested in digging deeper into the topic then I would suggest to go over "Java Concurrency in Practice by Brian Goetz" book.

about 4 years ago · Santiago Trujillo Report

0

Marking a variable as volatile is a hint for the JVM, to flush/sync the related segments of cache between threads/cores when that variable is updated. Marking stop as volatile then has better behaviour (but not perfect, you may have some extra executions on your threads before they see the update).

Marking t as volatile puzzles me as to why it works, it may be that because this is such a small program, t and stop are in the same row in the cache, so when one gets flushed/synced the other does too.

System.out.println is thread safe, so there is some synchronization going on internally. Again, this may be causing some parts of the cache to be synced between the threads.

If anyone can add to this, please do, I also would really like to hear a more detailed answer on this.

about 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!