2014-10-22 4 views
1

мне нужно ждать, пока список нити не прекращается, но мой код работает только тогда, когда сон не является постоянной величиной я хочу knwo почему, вот мой тест Класса:Подождите, пока список нити не конец

Если я изменить 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 

И этот процесс не прекращается

+0

Я не знаком с многопоточными, так что, пожалуйста ... –

+0

Почему бы вам просто не использовать обратный отсчет? Инициализируйте его количеством потоков и сделайте потоки подсчитанными при завершении. Затем ждите на защелке - вуаля, проблема решена. – Ordous

+0

'lock.notify' ничего не делает, если никто не ждет. Это относится к вашему журналу 'notify 7'. Когда вы вызываете 'wait', вы отпускаете фиксатор на' lock', который, в свою очередь, берется двумя нитями, уже ожидающими в затворе блока 'synchronized'.Поэтому один из потоков 'notify()' тратится впустую. – njzk2

ответ

4

Вот что происходит:

  1. Все «рабочий» резьб и сон
  2. Замок приобретает главный поток через synchronized (lock)
  3. Основной релиз нить замок по телефону lock.wait() и ждать для уведомления.
  4. Один из рабочих приобретает замок, вызывает уведомление и завершает работу.
  5. На данный момент его чистый случайный: основной поток может получить блокировку и увеличить счетчик, или один из рабочих может получить блокировку и позвонить notify.

код с комментариями:

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

Технически это не тупик, поскольку все дочерние потоки были бы завершены, пока родитель все еще ждет освобождения блокировки. Но так как ни один другой поток не держит ресурс, это собственная ошибка родительского потока, что он не может закончить изящно. –

+0

Большое спасибо, не могли бы вы написать правильный код, чтобы подождать, пока не закончится все остальные потоки? и это не упражнение, я действительно нуждаюсь в этом –

+0

Его работы идеальны, спасибо –

1

К моему воспоминанию, notify() уведомит только один поток - без какого-либо контроля над которой нити уведомление. Поэтому вы не можете быть уверены, что правильный поток уведомляется о том, что блокировка была выпущена.

Если вы хотите, чтобы все потоки были уведомлены, используйте notifyAll().

Учитывая, что у объекта блокировки есть только одно(), проблема заключается в том, сколько раз вы на самом деле «ожидаете» и/или уведомляетесь. Когда дочерний процесс освобождает блокировку, нет гарантии, что родительский поток (который ждет) будет первым, кто проснется, прежде чем другой дочерний поток продолжит свой синхронизированный блок. Ваш родительский поток ждет разбудить 10 раз, но если другой поток «перехватит» ваше ожидаемое уведомление, вы никогда не получите все 10, и ваш цикл никогда не сломается.

При использовании разных сроков сна для каждой темы вы даете основной теме достаточно времени для завершения кода после lock.wait() и повторно введите while(true) цикл, чтобы ждать в замке в другой раз.

Если это упражнение при использовании объектов блокировки, это одно. В противном случае вы можете использовать метод Thread join(), чтобы убедиться, что все потоки завершены до продолжения.

+0

Но есть только 1 поток, ожидающий' lock': основной поток. Все остальные потоки просто вызывают 'notify()' на нем, они не 'wait()' на нем. – icza

+0

Я уже пытаюсь с уведомлением. Тот же результат, его работы, только если сон различен. –

+0

Извините, я пропустил это. Я обновил свой ответ. –

0

я наоборот :) с 200 заканчивает процесс, но с произвольным низким число это не так.

Я думаю, что происходит в том, что одна или несколько нитей запустить их:

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

до того, как поток, который был уведомлен получает снова запустить.

Это может быть: Java : Does wait() release lock from synchronized block (первый ответ)

реквизита для cohadar " Thread приобретает внутреннюю блокировку, когда он входит в синхронизированный метод Thread внутри синхронизированного метода устанавливаются как собственник. блокировка и находится в состоянии RUNNABLE. Любой поток, который пытается ввести заблокированный метод, становится BLOCKED.

Когда потоковые вызовы ждут, он освобождает текущий замок объекта (он держит все блокировки от других объектов), а не переходит в состояние ОЖИДАНИЯ.

Когда некоторые другие вызовы потоков уведомляют или уведомляют об этом же самом объекте, первый поток изменяет состояние с WAITING на BLOCKED, Notified thread НЕ автоматически перезагружает блокировку или становится RUNNABLE, на самом деле он должен бороться за блокировку со всеми другими заблокированными потоками , . "

ваш уведомит будет толкать ожидающий поток в„заблокирован“это то же самое заблокированное состояние, что потоки, ожидающие на синхронизируются ждет на

, если какой-либо из нитей, которые делают:.

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

закончить и запустить их предупредить, то это не влияет больше. ожидающий поток уже проснулся. так что вы потеряете некоторые из них извещает. (они только посылают один раз Afterall).

если вы с укажите время вашей продолжительности сна, вы можете перетасовать таким образом, чтобы ваша ожидающая нить оказалась счастливой. но это случайное.

0

Для простоты предположим, что ваш основной() запускает два потока и ждет два раза вместо запуска десяти потоков и ожидания десять раз. Вот что может случиться.

  • Основной поток запускает десять детей
  • основной поток вызовов Wait()
  • один ребенок просыпается, звонки Notify(), завершается,
  • другой ребенок просыпается, вызовы уведомит() завершается,
  • основной поток просыпается (Wait() возвращает)
  • основные вызовы Wait() во второй раз ....

На этом этапе main() будет ждать() навсегда, потому что нет детей, оставшихся для уведомления().

Смежные вопросы