简体   繁体   中英

Java synchronized block, not all the threads get terminated

So I have the following code:

import java.lang.Thread;
import java.lang.Integer;

class MyThread extends Thread {
private int id;

MyThread(int i){
    id = i;
    }

public void run() {
    while(true){
        try{                    
            synchronized(Global.lock){
                Global.lock.wait();
                if(Global.n == 0) {System.out.println(id); Global.lock.notify(); break;}
                --Global.n;
                System.out.println("I am thread " + id + "\tn is now " + Global.n);
                Global.lock.notify();
                }
            }
        catch(Exception e){break;}
        }
    }
}

class Global{
public static int n;
public static Object lock = new Object();
}

public class Sync2{
public static final void main(String[] sArgs){
    int threadNum = Integer.parseInt(sArgs[0]);
    Global.n = Integer.parseInt(sArgs[1]);

    MyThread[] threads = new MyThread[threadNum];

    for(int i = 0; i < threadNum; ++i){
        threads[i] = new MyThread(i);
        threads[i].start();     
        }
    synchronized(Global.lock){Global.lock.notify();}
}
}

two parameters are entered: a number n and the number of threads to be created. Every thread decreases n by one and then passes control. All threads should stop when n is 0. It seems to work fine so far, but the only problem is that in most of the cases all threads except one terminate. And one is hanging on. Any idea why?

And yes, this is part of a homework, and that is what I've done so far (I was no provided with the code). I'am also explicitly restricted to use a synchronized block and only wait() and .notify() methods by the task.

EDIT: modified the synchronized block a bit:

synchronized(Global.lock){
  Global.lock.notify();
  if (Global.n == 0) {break;}
  if (Global.next != id) {Global.lock.wait();  continue;}
  --Global.n;
  System.out.println("I am thread " + id + "\tn is now " + Global.n);
  Global.next = ++Global.next % Global.threadNum;
  }

now threads act strictly in the order they are created. Its pretty unclear from the task wording, but might be the right thing.

You have a race condition. Think about what happens with a single worker thread. Global.n is set to 1 and then the thread starts. It immediately goes into a wait state. Suppose, though, that notify() had already been called on the main thread. Since the worker thread hasn't yet entered a wait state, it isn't notified. Then, when it finally does call wait() , there are no other threads around to call notify() , it stays in the wait state forever. You need to fix up your logic to avoid this race condition.

Also, do you really want a single worker thread to decrement Global.n more than once? That can easily happen with your while (true) ... loop.

EDIT You also have another logic problem with a single thread. Suppose it enters the wait state and then the notify() in main is called. It wakes the worker thread which decrements Global.n to 0, calls notify() , and then goes back to waiting. The problem is that notify() didn't wake any other thread because there were no other threads to wake. So the one worker thread will wait forever. I haven't analyzed it fully, but something like this might also happen with more than one worker thread.

You should never have a naked wait() call, as semaphores in java are not cached. wait() should always be nested in some sort of

while (condition that you are waiting on)
    obj.wait();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM