2016-08-19 2 views
1

Я пытался напечатать четные и нечетные числа двумя повторяющимися повторами, используя wait и notify. Тем не менее, я прошел через все реализации, приведенные на веб-сайте. Хотя в качестве первого разработчика многопоточности я пытался сделать это сам, но я не смог получить желаемый результат. Здесь я вставляю свой код ниже: Не могли бы вы просмотреть и вернуться с исправлениями и объяснениями, где я допустил ошибку.Печать Даже нечетное число с использованием двух потоков

package com.test.printEvenOdd; 

public class PrintOddEvenNumbers { 

    public static void main(String[] args){ 

     String s = new String(""); 

     EvenThread t1= new EvenThread(s); 
     OddThread t2= new OddThread(s); 
     Thread th1 = new Thread(t1); 
     Thread th2 = new Thread(t2); 
     th1.start(); 
     th2.start(); 
    } 

} 

class EvenThread implements Runnable{ 
    String s; 
    EvenThread(String s){ 
     this.s= s; 
    } 

    @Override 
    public void run() { 
     synchronized(s){ 
      for(int i=1;i<=10;i++){ 

       if(i%2==0){ 
        try { 
         Thread.sleep(50); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
        System.out.println(i); 
        s.notify(); 
       } 
       try { 
        s.wait(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 

     } 
    } 

} 

class OddThread implements Runnable{ 

    String s; 
    OddThread(String s){ 
     this.s= s; 
    } 

    @Override 
    public void run() { 
     synchronized(s){ 
      for(int i=1;i<=10;i++){ 
       try { 
        Thread.sleep(50); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
       if(i%2==1){ 
        System.out.println(i); 
        s.notify(); 
       } 
       try { 
        s.wait(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

} 
+2

Какой выход вы ожидаете? Какой результат вы получаете? Почему вы не следуете рекомендуемым шаблонам (например, ждать в цикле while, который проверяет состояние и т. Д.)? – assylias

+1

http://stackoverflow.com/questions/6017281/odd-even-number-printing-using-thread –

+0

Вам нужно добавить желаемый результат и фактический результат. –

ответ

2

Ваша проблема заключается в том, что вы замок является слишком консервативным/ограничительно:

Ставит замок вокруг всего цикла; для обоих потоков.

Итак, одна нить попадает в петлю; но быстро он не может прогрессировать. Потому что для этого потребуется другой поток. Но второй поток не может даже начаться - потому что он может войти в свой цикл вообще!

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

Это похоже на строительство комнаты, в которой только два человека могут выйти вместе; но тогда вы позволяете одному человеку войти в эту комнату.

Добро пожаловать в многопоточное программирование; вы только что создали свой первый dead-lock.

И для записи: при переустановке замков; убедитесь, что вы правильно передаете сигнал; так что wait/notify может работать так, как предполагалось.

И наконец: если вы внимательно посмотрите на свой код; вы обнаружите, что вы дублировали много кода. Это всегда плохая идея. Вместо этого: попытайтесь определить, какие части действительно разные; и все остальное ... должно существовать точно после в вашем исходном коде. Итак, в качестве другого упражнения: когда вы перестроили свой код так, чтобы он выполнял то, что он должен был делать, попробуйте, если вы можете его реорганизовать, чтобы уменьшить количество дубликатов кода. Я гарантирую вам, что это будет упражнение, на которое стоит потратить ваше время!

+0

Да. Спасибо за уточнение. Я немного смутился о сигнальной передаче. поэтому при написании кода он не мог правильно его расположить. Сейчас я работаю над этим. – RoyalTiger

+0

Привет, у меня есть еще одно сомнение. В приведенном ниже коде я реализовал связь между потоками, используя wait() -notify(), и это дает мне ожидаемый результат. Мой вопрос в том, есть ли какая-либо гарантия, что всегда «Главная тема» получит первый шанс выполнить, так как планирование потоков зависит от jvm. И если «детская нить» получит первый шанс, сигнал notify() будет пропущен, а «главный поток» будет ждать навсегда. Как я могу подтвердить, что «Main thread» будет выполняться первым всегда. – RoyalTiger

+0

Код приведен ниже: package com.test.Thread; общественного класса ThreadExample1 { \t \t государственной статической силы основных (String [] арг) бросает InterruptedException { \t \t \t \t ThreadChild1 т = новый ThreadChild1(); \t \t t.start(); \t \t синхронизированы (т) { \t \t \t для (INT = 1; г <10; я ++) { \t \t \t \t System.out.println ("Основной" + I); \t \t \t \t \t \t t.wait(); \t \t \t System.out.println («Main получил уведомление»); \t \t \t \t} \t \t \t} } класс ThreadChild1 расширяет тему { \t \t общественности недействительным запуска() { \t \t синхронизирована (это) { \t \t \t для (INT I = 1; i <10; i ++) { \t \t \t \t System.out.println («Ребенок» + i); \t \t \t} \t \t \t this.notify(); \t \t \t} \t} } – RoyalTiger

1

Вы должны переместить «wait()» внутри блока «if». Остальная нить войдет, чтобы ждать, не уведомляя другой ожидающий поток, и оба они будут ждать.

 if(i%2==0){ 
      synchronized(s){ 
       System.out.println(i); 
       try { 
        s.notify(); 
        s.wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 

Имеются проблемы с кодом. Не нужно спать. Как упоминалось в предыдущем ответе, вы слишком нетерпеливо синхронизировались, что не нужно. Нет никакой гарантии, начнется ли даже поток сначала с первого или нечетного потока. Это зависит от того, какой поток ничем не удалось получить. В конце концов, один поток будет ждать навсегда, пока другой поток уже выйдет, и после этого никто не будет уведомлять. И любой код wait() должен обрабатывать ложное пробуждение. here

+0

Благодарим за предложение. Я прошел через ссылку, данную в jenkov. Означает ли это, что каждый раз, когда я использую wait(), я должен охранять внутри цикла, чтобы избежать ложного пробуждения? Но я не понимал, какие могут быть случаи использования Spurious. – RoyalTiger

1

Существует ряд проблем с исходным кодом. См. Ответ GhostCat для объяснения их. В общем, такого рода вычисления не подходят для многопоточности, поскольку вы (по-видимому) хотите, чтобы числа печатались последовательно. Но, учитывая это желание и желание использовать 2 потока, чередующихся для этого, вы можете сделать это следующим образом. Обратите внимание, что все еще есть проблемы с этим решением.Поток зависит от другого потока, выполненного, чтобы иметь возможность достичь своего конечного условия, что означает, что если вы только создали его для нечетных (или четных) чисел, вы переходите в бесконечный цикл.

import java.util.Objects; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.function.IntPredicate; 

public class Foo { 

    public static void main(String[] args) { 
     // an executor service will handle the thread pool and scheduling 
     ExecutorService pool = Executors.newFixedThreadPool(2); 
     pool.submit(new NumberPrintAndIncrement(i -> i % 2 != 0)); 
     pool.submit(new NumberPrintAndIncrement(i -> i % 2 == 0)); 
     // you want to shut down the pool when the threads are done 
     pool.shutdown(); 
    } 
} 

final class NumberPrintAndIncrement implements Runnable { 

    // Need a shared lock for accessing and updating the current number 
    private static final Object LOCK = new Object(); 
    // The number is shared between threads so it needs to be volatile 
    private static volatile int number = 1; 

    // Instance variable for letting a particular runnable know if it should 
    // print the number in it's current state 
    private final IntPredicate predicate; 

    NumberPrintAndIncrement(IntPredicate predicate) { 
     this.predicate = Objects.requireNonNull(predicate); 
    } 

    @Override 
    public void run() { 
     while (number < 10) { 
      // this could run at any point and any number of times, but 
      // that doesn't matter since it is just doing a quick check and 
      // a possible update. If the number doesn't satisfy the predicate, 
      // this will just be a no-op. Having a predicate means 
      // you don't have to rely on wait and notify to try and 
      // achieve interleaving the number output properly which 
      // is good due to the liveness problem Rajesh mentioned. 
      synchronized (LOCK) { 
       if (predicate.test(number)) { 
        System.out.println(number); 
        number++; 
       } 
      } 
     } 
    } 
} 
1

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

public class PrintOddEvenNumbers { 

    public static void main(String[] args){ 

     String s = new String(""); 

     EvenThread t1= new EvenThread(s); 
     OddThread t2= new OddThread(s); 
     Thread th1 = new Thread(t1); 
     Thread th2 = new Thread(t2); 
     th1.start(); 
     th2.start(); 
    } 

} 

class EvenThread implements Runnable{ 
    String s; 
    EvenThread(String s){ 
     this.s= s; 
    } 

    @Override 
    public void run() { 
     synchronized(s){ 
      for(int i=1;i<=10;i++){ 
       System.out.println("EvenThread i: " + i); 

       if(i%2==0){ 
        try { 
         Thread.sleep(50); 
        } catch (InterruptedException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
        } 
        System.out.println(i); 
        System.out.println("EvenThread notify"); 
        s.notify(); 
       } 
       try { 
        System.out.println("EvenThread waiting.."); 
        s.wait(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 

     } 
    } 
} 

class OddThread implements Runnable{ 

    String s; 
    OddThread(String s){ 
     this.s= s; 
    } 

    @Override 
    public void run() { 
     synchronized(s){ 
      for(int i=1;i<=10;i++){ 
       System.out.println("OddThread i: " + i); 

       try { 
        Thread.sleep(50); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
       if(i%2==1){ 
        System.out.println(i); 
        System.out.println("OddThread notify"); 
        s.notify(); 
       } 
       try { 
        System.out.println("OddThread waiting.."); 
        s.wait(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 
} 

Это будет печатать:

EvenThread i: 1 
EvenThread waiting.. 
OddThread i: 1 
1 
OddThread notify 
OddThread waiting.. 
EvenThread i: 2 
2 
EvenThread notify 
EvenThread waiting.. 
OddThread i: 2 
OddThread waiting.. 

простое объяснение:

  • Когда OddThread достигает в i из , он waits для s быть освобожден.
  • Когда EvenThread достигает i от , он также waits для s будет выпущен.

Теперь у вас есть оба потока ждут, чтобы разбудить (затор).

Это происходит из-за условий, которые необходимо выполнить, чтобы разбудить другую ожидающую резьбу вверх, используя notify, то есть i%2==1 и i%2=0.

Это не единственная проблема, однако есть и некоторые фундаментальные проблемы.

  • Использование потоков в данном конкретном случае, если бы это было в производстве неверен, как вы пытаетесь сделать последовательную работу в любом случае, поэтому накладные расходы создавать темы для каждой задачи добавляет излишнюю нагрузку.
  • Нет общего доступа к ресурсам, что делает synchornize избыточным.
  • Вы ожидаете, что один поток будет удерживать замок перед другим, это не то, как работает Thread. Это может быть либо тот, который сначала удерживает блокировку.
0

У меня есть еще одно сомнение. В приведенном ниже коде я реализовал связь между потоками, используя wait() -notify(), и это дает мне ожидаемый результат. Мой вопрос в том, есть ли какая-либо гарантия, что всегда «Главная тема» получит первый шанс выполнить, так как планирование потоков зависит от jvm. И если «детская нить» получит первый шанс, сигнал notify() будет пропущен, а «главный поток» будет ждать навсегда. Как я могу подтвердить, что «Main thread» будет выполняться первым всегда.

package com.test.Thread; 

public class ThreadExample1 { 

    public static void main(String[] args) throws InterruptedException{ 

     ThreadChild1 lockingObj = new ThreadChild1(); 
     lockingObj .start(); 
     synchronized(lockingObj){ 
      for(int i=1;i<10;i++){ 
       System.out.println("Main "+i); 
      } 
      lockingObj.wait(); 
      System.out.println("Main got notified"); 

     } 

    } 

} 

class ThreadChild1 extends Thread{ 

    public void run(){ 
     synchronized(this){ 
      for(int i=1;i<10;i++){ 
       System.out.println("Child "+i); 
      } 
      this.notify(); 
      } 
    } 
} 
Смежные вопросы