How to use wait and notify protocol with multiple threads

In particular, can someone tell me what is wrong with this piece of code. It should start threads, so it should print β€œEnter thread ..” 5 times, and then wait until notification () is called. But he accidentally prints "Enter .." and "Done" and is still waiting for others.

public class ThreadTest implements Runnable { private int num; private static Object obj = new Object(); ThreadTest(int n) { num=n; } @Override public void run() { synchronized (obj) { try { System.out.println("Entering thread "+num); obj.wait(); System.out.println("Done Thread "+num); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Runnable tc; Thread t; for(int i=0;i<5;i++) { tc = new ThreadTest(i); t = new Thread(tc); t.start(); } synchronized (obj) { obj.notifyAll(); } } } 
+6
java multithreading synchronization wait notify
source share
3 answers

You are not doing anything wrong with calling a method, but you have a condition.

Although in an ideal world the main thread will reach its synchronized block after all worker threads have reached the wait () call, there is no guarantee of this (you explicitly told the virtual machine that you do not want the threads to execute sequentially with the main thread, creating threads for it) . It may happen (for example, if you have only one core) that the thread scheduler decides to block all worker threads immediately to start the main thread. Workflows may have been disabled in context due to a cache miss. Perhaps one worker thread blocks I / O (the print statement), and the main thread switches to its place.

Thus, if the main thread manages to reach the synchronized block before all the worker threads have reached the wait () call, then those worker threads that have not reached the wait () call will not work as intended. Since the current setup does not allow you to control this, you must add an explicit reference to this. You can either add some kind of variable that increases, since each worker thread reaches wait (), and the main thread does not call notifyAll () until this variable reaches 5, or you can have the main thread loop and call notifyAll again ( ) so that worker threads are freed up in several groups.

Look in the java.util.concurrent package - there are several lock classes that provide more powerful features than basic synchronized locks. More than ever, Java saves you from re-creating the wheel. CountDownLatch seems especially relevant.

Therefore, concurrency is difficult. You have to design to make sure everything still works when threads are executed in requests that you don't need, as well as orders that you would like.

+8
source share

The second recommendation is CountDownLatch. Here is the pattern I use for my multi-threaded tests:

 final int threadCount = 200; final CountDownLatch startPistol = new CountDownLatch(1); final CountDownLatch startingLine = new CountDownLatch(threadCount); final CountDownLatch finishingLine = new CountDownLatch(threadCount); // Do a business method... Runnable r = new Runnable() { public void run() { startingLine.countDown(); try { startPistol.await(); // TODO: challenge the multithreadedness here } catch (InterruptedException e) { Thread.interrupted(); } finishingLine.countDown(); } }; // -- READY -- for (int i = 0; i < threadCount; i++) { Thread t = new Thread(r); t.start(); } // Wait for the beans to reach the finish line startingLine.await(1000, TimeUnit.MILLISECONDS); // -- SET -- // TODO Assert no one has started yet // -- GO -- startPistol.countDown(); // go assertTrue(finishingLine.await(5000, TimeUnit.MILLISECONDS)); // -- DONE -- // TODO: final assert 

The idea is to ensure that the next line of code that your threads will execute is a task that challenges multithreading or as close to it as possible. I think of streams as runners on a track. You get them on the track (create / run), wait for them to finally line up the line in the start line (everyone called startLine.countDown ()), then they start the trigger gun (startPistol.countDown ()) and wait for everyone to cross the finish line (finishLine. countDown ()).

[EDIT] It should be noted that if you do not have the code or checks that you want to perform between startLine.await () and startPistol.countDown (), you can combine both startLine and startPistol into one CyclicBarrier (threadCount + 1). The dual approach of CountDownLatch is practically the same and allows the main test thread to perform any settings / checks after all other threads are lined up and before they start working, if necessary.

+2
source share

The root problem with your code is that some of the threads don't end up in wait until the main thread calls notifyAll . Therefore, when they wait, nothing will wake them.

To do this work (using wait / notify), you need to synchronize the main thread so that it waits until all the child threads have received a state in which they can receive a notification before it makes this call.

Generally speaking, synchronizing with wait , notify and primitive locks is more complicated. In most cases, you will get better results (i.e. simpler, more reliable, and more efficient code) using the Java concurrency utility classes.

0
source share

All Articles