Is there a race condition in this example? If so, how can this be avoided?

I look at some notifications / wait examples and stumbled upon this. I understand that a synchronized block essentially defines a critical section, but isn't that a race condition? Nothing indicates which synchronized block was first entered.

public class ThreadA { public static void main(String[] args){ ThreadB b = new ThreadB(); b.start(); synchronized(b){ try{ System.out.println("Waiting for b to complete..."); b.wait(); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println("Total is: " + b.total); } } } class ThreadB extends Thread { int total; @Override public void run(){ synchronized(this){ for(int i=0; i<100 ; i++){ total += i; } notify(); } } } 

Website Access:

Waiting for b to complete ...

Total: 4950

+7
java multithreading synchronization synchronized race-condition
source share
3 answers

Correctly, this does not guarantee which thread will be executed first. Thread b can make its notification before the main thread ever starts to wait.

In addition to this, the thread may return from waiting without being notified, so setting the flag and checking it before entering the expected is technically insufficient. You can rewrite it to something like

 public class ThreadA { public static void main(String[] args) throws InterruptedException { ThreadB b = new ThreadB(); b.start(); synchronized(b){ while (!b.isDone()) { System.out.println("Waiting for b to complete..."); b.wait(); } System.out.println("Total is: " + b.total); } } } class ThreadB extends Thread { int total; private boolean done = false; @Override public void run(){ synchronized(this){ for(int i=0; i<100 ; i++){ total += i; } done = true; notify(); } } public boolean isDone() {return done;} } 

so the main thread will wait for b to be executed with its calculation, regardless of who starts over.

By the way, the API documentation recommends not synchronizing threads. JDK synchronizes threads to implement Thread # join. The thread that completes sends a notification that everything that joins it receives. If you triggered a notification or notified about it from the thread on which you acquired the lock, then joining it may return earlier. One of the side effects here is that if you delete the notification, the code works the same.

+5
source share

Yes, this is a race condition. Nothing prevents ThreadB from starting, entering its execution method, and synchronizing itself to ThreadA from entering its synchronized block (thus waiting indefinitely). However, this is unlikely to ever happen, given the time it takes to start a new thread.

The easiest and most recommended way to deal with this situation is not to write your own implementation, but to choose the use of the called / future provided by the Contractor.

To fix this particular case without the following standards:

  • Set the Boolean value to be set at the end of the synchronized ThreadB.
  • If the logical "complete" is true after entering the synchronized block, you should not call wait.
+2
source share

Yes - this is a race as to which thread enters the first synchronized block. For most race scenarios, the result and answer will be the same. For one, however, the program will be deadlocked:

  • Main launches b.start () calls and schedules it immediately.
  • Thread B starts, is entered synchronously, calls notify ().
  • Main enters its synchronized block, calls wait ()

In this case, main will wait forever, since thread b caused a notification before main is blocked in wait ().

However, this is unlikely - but with all threads, you must conclude that this will happen, and then at the worst time.

+2
source share

All Articles