简体   繁体   中英

Wait until a list of thread end

I need to wait until a list of thread terminate, but my code works only if the Sleep is not constant i want to knwo why, here my Class test :

If i change Thread.sleep(200); ---> Thread.sleep(i*b); its works fine !?

public class TestThread {
    public static void main(String[] args) {
        Object lock = new Object();     
        for ( int p=0; p<10; p++) {     
            final int i=p;
            new Thread(new Runnable() {                 
                @Override
                public void run() {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("notify "+i);
                    synchronized(lock){                             
                        lock.notify();
                    }   
                }
            }).start();             
        }
        Integer counter=0;
        synchronized (lock) {
            try {               
                while(true){
                    System.out.println("Before wait");                  
                    if (counter==10)//wait until all threads ends
                        break;
                    lock.wait();
                    counter += 1;
                    System.out.println("After wait "+counter);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }   
        }
        System.out.println("End");
    }
}

The result with notifyAll()

Before wait
notify 3
notify 7
After wait 1
Before wait
notify 1
notify 2
notify 0
After wait 2
Before wait
notify 4
After wait 3
Before wait
After wait 4
Before wait
notify 5
After wait 5
Before wait
notify 6
notify 8
After wait 6
Before wait
notify 9
After wait 7
Before wait
After wait 8
Before wait

And the process is not terminated

Here is what happens:

  1. All "worker" thread start and sleep
  2. The lock is acquire by the main thread through synchronized (lock)
  3. The main thread release the lock by calling lock.wait() and wait for a notification.
  4. One of the worker acquire the lock, call notify and terminates.
  5. At this point, its pure random: the main thread could acquire the lock and increase the counter, or one of the worker could get the lock and call notify .

Code with comments:

 public static void main(String[] args)
{
    final Object lock = new Object();
    for (int p = 0; p < 10; p++)
    {
        //You start each thread. They all go to sleep for 200ms.

        final int i = p;
        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    Thread.sleep(200);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                System.out.println("notify " + i);
                synchronized (lock)
                {
                    lock.notify();
                }
            }
        }).start();
    }
    //At this point, all the thread are sleeping.



    Integer counter = 0;
    synchronized (lock)//The main thread acquire the lock, so even if the other thread wakes up, they will wait for the lock
    {
        try
        {
            while (true)
            {
                System.out.println("Before wait");
                if (counter == 10)// wait until all threads ends
                    break;
                lock.wait();// Object.wait() will release the lock on the object.
// So 1 of the thread will acquire the lock, call notify(), and release the lock.
 // But you have no guarantee that the main thread will reacquire the lock right away !!     
//its possible that all remaining waiting thread gets the lock and call notify(), before the main thread get 
//a chance to continue. This is why, you may end up with a deadlock

                counter += 1;

                System.out.println("After wait");
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
    System.out.println("End");
}

Here is a better way to handle this:

final List<Thread> workers = new ArrayList<Thread>();
for (int p = 0; p < 10; p++)
    {
        final int i = p;
        final Thread t = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    Thread.sleep(200);//or do something a bit more useful
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        });
        workers.add(t);
        t.start();
    }

    for(Thread t : workers)
       t.join();//wait until the thread finishes

To my recollection, notify() will only notify a single thread - without having any control over which thread is being notified. So you cannot be sure that the correct thread is being notified that the lock has been released.

If you want to ensure that all threads are notified, use notifyAll() .

Given that you only have a single wait() on the lock object, the issue is how many times are you actually "waiting" and/or notified. Once a child process releases a lock, there is not guarantee that the parent thread (that is waiting) will be the first to wake up before another child thread continues through it's synchronized block. Your parent thread is waiting to be woken up 10 times, but if another thread "intercepts" your expected notification, you'll never receive all 10 and your loop will never break.

By using different sleep times for each thread, you are giving your main thread enough time to complete the code after the lock.wait() and reenter it's while(true) loop to wait on a lock another time.

If this is an exercise in using lock objects, that's one thing. Otherwise you can use Thread's join() method to ensure all threads are completed before continuing.

I get the opposite :) with 200 the process finishes, but with an arbitrary low number it does not.

I think what is happening is that one or more threads run their:

synchronized(lock){                             
    lock.notify();
}

before the thread that was notified gets to run again.

This can be because: Java : Does wait() release lock from synchronized block (first answer)

props to cohadar " Thread acquires the intrinsic lock when it enters a synchronized method. Thread inside the synchronized method is set as the owner of the lock and is in RUNNABLE state. Any thread that attempts to enter the locked method becomes BLOCKED.

When thread calls wait it releases the current object lock (it keeps all locks from other objects) and than goes to WAITING state.

When some other thread calls notify or notifyAll on that same object the first thread changes state from WAITING to BLOCKED, Notified thread does NOT automatically reacquire the lock or become RUNNABLE, in fact it must fight for the lock with all other blocked threads. "

your notify will push your waiting thread into "blocked". this is the same blocked state that the threads waiting on synchronized are waiting on.

if any of the threads that do:

synchronized(lock){                             
    lock.notify();
}

finish and run their notify, then it has no effect anymore. the waiting thread is already woken up. so you lose some of their notifies. (they only get sent once afterall).

if you change the timing of your sleep duration, you might end up shuffling in such a way that your waiting thread ends up the lucky one. but this is random.

For sake of simplicity, let's say your main() starts two threads and waits two times instead of starting ten threads and waiting ten times. Here's what can happen.

  • Main thread starts ten children
  • Main thread calls wait()
  • one child wakes up, calls notify(), terminates,
  • another child wakes up, calls notify(), terminates,
  • main thread wakes up (wait() returns)
  • main calls wait() a second time....

At this point, main() is going to wait() forever because there are no children left to notify() it.

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