等到线程列表结束

时间:2014-10-22 14:11:39

标签: java multithreading

我需要等到一个线程列表终止,但是我的代码只有在Sleep不是常数时才能工作我想知道为什么,这里我的Class测试:

如果我改变Thread.sleep(200); --->了Thread.sleep(I * B);它的工作正常!?

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");
    }
}

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

并且该过程未终止

4 个答案:

答案 0 :(得分:4)

以下是发生的事情:

  1. 所有&#34;工人&#34;线程启动和睡眠<​​/ li>
  2. 主线程通过synchronized (lock)
  3. 获取锁定
  4. 主线程通过调用lock.wait()释放锁定并等待通知。
  5. 其中一名工人获得锁定,致电通知并终止。
  6. 此时,它是纯随机的:主线程可以获取锁定并增加计数器,或者其中一个工作人员可以获得锁定并调用notify
  7. 带注释的代码:

     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");
    }
    

    以下是处理此问题的更好方法:

    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
    

答案 1 :(得分:1)

在我的回忆中,notify()只会通知单个线程 - 而无法控制通知哪个线程。因此,您无法确定是否已通知正确的线程已释放锁定。

如果要确保通知所有线程,请使用notifyAll()

鉴于你在锁定对象上只有一个wait(),问题是你实际上有多少次等待&#34;和/或通知。一旦子进程释放锁定,就不能保证在另一个子线程继续通过它的同步块之前,父线程(正在等待)将是第一个被唤醒的线程。你的父线程等待被唤醒10次,但如果另一个线程&#34;拦截&#34;你预期的通知,你永远不会收到所有10,你的循环永远不会中断。

通过为每个线程使用不同的休眠时间,您为主线程提供了足够的时间来完成lock.wait()之后的代码并重新输入它的while(true)循环以等待另一个锁定时间。

如果这是使用锁定对象的练习,那就是一件事。否则,您可以使用Thread的join()方法确保在继续之前完成所有线程。

答案 2 :(得分:0)

我得到相反的结果:) 200过程完成,但是任意低的数字都没有。

我认为发生的事情是一个或多个线程运行:

synchronized(lock){                             
    lock.notify();
}
在被通知的线程再次运行之前

这可能是因为: Java : Does wait() release lock from synchronized block (第一个答案)

支持cohadar “ 线程在进入同步方法时获取内部锁。 synchronized方法中的线程被设置为锁的所有者并处于RUNNABLE状态。尝试输入锁定方法的任何线程都会变为BLOCKED。

当线程调用等待时,它释放当前对象锁(它保留所有来自其他对象的锁),然后进入WAITING状态。

当某个其他线程在同一个对象上调用notify或notifyAll时,第一个线程将状态从WAITING更改为BLOCKED,Notified线程不会自动重新获取锁定或变为RUNNABLE,实际上它必须为所有其他被阻塞的线程争取锁定。 “

您的通知会将您的等待线程推送到“已阻止”状态。这是等待同步的线程等待的阻塞状态。

如果执行以下任何线程:

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

完成并运行他们的通知,然后它就不再有效了。等待线程已经被唤醒了。 所以你失去了一些通知。 (他们只会在事后发送一次)。

如果你改变睡眠持续时间的时间,你最终可能会以等待线程结束幸运者的方式进行洗牌。但这是随机的。

答案 3 :(得分:0)

为简单起见,让我们说你的main()启动两个线程并等待两次而不是启动十个线程并等待十次。这是可能发生的事情。

  • 主线程开始十个孩子
  • 主线程调用wait()
  • 一个孩子醒来,调用notify(),终止,
  • 另一个孩子醒来,调用notify(),终止,
  • 主线程唤醒(wait()返回)
  • 主要呼叫wait()第二次....

此时,main()将永远等待()因为没有孩子可以通知()它。