notifyAll()の挙動について

このQ&Aのポイント
  • マルチスレッドにおいて、notifyAll()が他のスレッドにwaitの解除を通知する方法について知りたい。
  • sleep sortをマルチスレッドで実装しようとしているが、notifyAll()を使用しても正しくソートされない。
  • notifyAll()はすべてのスレッドに同時に告知をするわけではない可能性があるため、スレッドを同期させる他の方法を探している。
回答を見る
  • ベストアンサー

notifyAll()の挙動について

こんにちは、マルチスレッドを勉強し始めた者です。 私の見立てが間違っていなければ、表題のとおり、notifyAllがどのように他のスレッドにwaitの解除を通知しているかを知りたいです。 事の始まりはsleep sortをマルチスレッドで実装しようと思ったことです。 sleep sortは表示までの時間がずれないことが重要だと考えましたので、すべてのスレッドが一斉に動いたほうが良いと思いました。 そこでネットで調べたところ、waitとnotifyAllを使うことでこれが実現できると考えまして、この二つを用いて実装を試みました。 以下がコードになります。 環境:Windows 7 64bit IDE:Eclipse 4.5.2 JDK:1.8 【SleepSortSample.java】 package exp01; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; public class SleepSortSample { public synchronized void print(int n){ try { wait(); Thread.sleep(n*10); } catch (InterruptedException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } System.out.print(n+","); } public synchronized void note(){ notifyAll(); } public static void main(String...args) { SleepSortSample sleepSortSample = new SleepSortSample(); List<Integer> list = IntStream.range(0, 30).boxed().collect(Collectors.toList()); Collections.shuffle(list); list.stream().forEach(c -> System.out.print(c + ",")); System.out.println(); for (Integer integer : list) { new Thread(()->sleepSortSample.print(integer.intValue())).start(); } new Thread(sleepSortSample::note).start(); } } ------------------------------------------ しかしながら、このコードだと以下のように全くソートが行われていません。 【出力例】 17,19,18,24,13,0,27,14,6,29,28,20,5,25,12,16,26,23,3,22,10,9,21,1,11,8,2,15,4,7, 7,4,15,2,8,11,1,21,9,10,22,3,23,26,16,12,25,5,6,20,28,14,29,13,27,0,17,18,24,19, このことから察するにnotifyAllはすべてのスレッドに同時に告知をするものではないことと、この告知に意外と時間がかかる、と言う風に推察しました。 ですが、公式ドキュメントを読んでもそのことについての記述が無いため、この考えが正しいかどうかの確認が出来ませんでした。 どなたかご存知の方がいれば教えていただきたく思います。 それともし可能ならすべてのスレッドを同期させる良い方法も教えていただけると幸いです。 よろしくお願いします。 ※OKWAVEより補足:「Webシステム開発」についての質問です。

  • Java
  • 回答数1
  • ありがとう数2

質問者が選んだベストアンサー

  • ベストアンサー
  • wormhole
  • ベストアンサー率28% (1619/5652)
回答No.1

>このことから察するにnotifyAllはすべてのスレッドに同時に告知をするものではないことと、この告知に意外と時間がかかる、と言う風に推察しました。 synchronizedされていますから同時に動くことがないことに注意してください。 wait()は一時的にsynchronizedでのロック状態を一時的に解除し自スレッドが停止し他スレッドが動作できるようにします。 notifyAll()呼出しで他のスレッドに告知はされますが、wait()している他スレッドが再開できるのは、notifyAll()呼出しスレッドのsynchronizedでのロック状態解除後になります。 またwait()していた他スレッドはロックの再取得で奪い合いをしロックが取得できたスレッドが再開されロックを取得できなかったスレッドはロックの再取得のチャンスを待つことになります。

mental_HELLth
質問者

お礼

回答いただきましてありがとうございました! 理解しました! つまりこの処理は並列処理にはなっていない、ということですね? 結局今回の例で行けば30個スレッドがwaitしているところにnotifyAllがされると、ロックを取得できたものから順々にスレッドが実行されていく、という処理がなされると理解しました。 ということはnotifyAllは同時に実行してほしい時に使えるものではないと言うことですね。 ともあれ非常にわかりやすい回答でした! ありがとうございました。

関連するQ&A

  • スレッド1とスレッド2を交互に実行するには?

    スレッド1→スレッド2→スレッド1・・・と同期を取り合いながら交互にスレッドを動かしたいのですが、Thread.getState()を用いないで実現するよい方法はないでしょうか? どうしてもデッドロックが発生しうる方法しか思いつかず困っています。 例えば以下のプログラムで1212と交互に表示させたい場合です。 以下のプログラムは当然デッドロックになりますが、やりたいことを伝えるために載せておきます。 Object obj; //スレッド間通信用のobject class Test1 extends Thread {  public void run() {   while(true) {    System.out.print("1");    synchronized (obj) {     obj.notify();//相手をnotifyして     obj.wait();//自分はwait    }   }  } } class Test2 extends Thread {  public void run() {   while(true) {    System.out.print("2");    synchronized (obj) {     obj.notify();     obj.wait();    }   }  } } new Test1().start(); new Test2().start();

    • ベストアンサー
    • Java
  • 交互にスレッド実行 (どうしてちゃんと動いてる?)

    http://oshiete1.goo.ne.jp/qa2968378.html で質問した者です。 2つのスレッドで1と2を交互に実行する以下のプログラム。 final Object obj1 = new Object(); //スレッド間通信用のobject final Object obj2 = new Object(); //スレッド間通信用のobject2 Thread t1 = new Thread() {  public void run() {   try {    while(true) {     System.out.print("1");     synchronized (obj1) {      synchronized (obj2) {       obj2.notify();//相手をnotifyして      }      obj1.wait();//自分はwait     }    }   } catech (Exception e){} }; Thread t2 = new Thread() {  public void run() {   try {    while(true) {     System.out.print("2");     synchronized (obj2) {      synchronized (obj1) {       obj1.notify();      }      obj2.wait();     }    }   } catch (Exception e){}  } }; Thread t1 = new Test1(); Thread t2 = new Test2(); t1.start(); while(t1.getState() != Thread.State.WAITING); t2.start(); が思ったように動いてはいるものの、なぜ正常に動いているかがわかりません。 t1が wait() したときはobj1のロックをもっていて、t2の synchronized (obj1){obj1.notify()} ブロックに入れず待機するはずだと思うのですが。 なぜちゃんと動いているのでしょう?

    • ベストアンサー
    • Java
  • wait()したスレッドが起こされるタイミング

    以下のコードにおいて。 % java ThreadWaitTest 2 の場合はaと表示したまま止まりますが、 % java ThreadWaitTest 1 の場合はスレッドの終了に伴ってwait()が切れ、bまで表示します。 私の望む動作は前者です。 後者の場合、これはつまりスレッドをwaitした際の待機プールがスレッドオブジェクトの場合、そのスレッドの実行が終了すると待機プールにある待機スレッドを自動でnotify()してしまうことを意味していると思うのですが、この解釈はあっていますか? Java のAPIドキュメントに 「別のスレッドが notify メソッドまたは notifyAll メソッドを呼び出してこのオブジェクトのモニター上で待機するスレッドに通知を出すまで待機します」 とあるように、ユーザが明確的にnotifyしないと起こらないと思いこんでいました。 なぜこういう仕様になっているのでしょうか? また、この仕様について詳しく書いてあるサイトやドキュメントなどありませんでしょうか? public class ThreadWaitTest extends Thread {  public Object mutex = new Object();    public static void main(String[] args) {   try {    ThreadWaitTest test = new ThreadWaitTest();    test.start();    System.out.println("a");    switch (new Integer(args[0])) {    case 1:     synchronized(test) {      test.wait();     }     break;    case 2:     synchronized(test.mutex) {       test.mutex.wait();     }     break;    }    System.out.println("b");   } catch (InterruptedException ie) {    ie.printStackTrace();   }  }  public void run() {   try {    Thread.sleep(5000);   } catch (InterruptedException ie){    ie.printStackTrace();}   } }

    • ベストアンサー
    • Java
  • すいません。解説してください。

    いつも教えて頂き大変お世話になっております。 下記プログラムを解説して頂きたいのです。 特に最初の4行を詳しく教えて欲しいです。 何度もすみません。 ご回答のほど、宜しくお願い申し上げます。 OSはUbuntu18.04を使っています。 このプログラムは、他のプログラムも関係するのでしょうか? 色々とお手数かけます。 コンパイルしたら注意:Sample90.javaの操作は、未チェックまたは安全ではありません。とエラーメッセージが出ました。 何でも参考になります。 ご回答のほど、宜しくお願い申し上げます。 package sample; import java.util.List; import java.util.ArrayList; import java.util.LinkedList; public class Sample90 { public static void main(String[] args) { new Sample90().execute(); } public void execute() { List list = new ArrayList(); list.add("A"); list.add("B"); for (int i = 0; i < list.size(); i++) { String s = (String) list.get(i); System.out.print(s + " "); } System.out.println(""); list = new LinkedList(); list.add("A"); list.add("B"); for (int i = 0; i < list.size(); i++) { String s = (String) list.get(i); System.out.print(s + " "); } } }

  • Javaのプログラムについて教えてください。

    class SampleA implements Runnable { int tmp1 = 0; int tmp2 = 0; public synchronized void run(){ try{ if(tmp1 == 1){ System.out.print("は"); System.out.print("め"); else if (tmp1 == 2){ System.out.print("じ"); System.out.print("て");          } if(tmp2 == 0) tmp2 = tmp2 + 1; System.out.print("ま"); } System.out.print("。"); }catch(InterrupedException e){ System.out.print("し"); } } } //Sample2.java class Sample2{ public static void main(String[] args){ Sample1 sp = new Sample1(); Thread th1 = new Thread(sp); Thread th2 = new Thread(sp); th1.start(); th2.start(); } } wait(),notify(),throwを使って例外を生じさせるところまではわかったのですが このプログラムを「はじめまして。」と順番に出力するにはどうすればいいですか?

  • sleepメソッドを呼び出すには?

    JAVAのプログラムを勉強しているのですが、ArrayList<Character>の各要素に対してsleepメソッドを呼び出したいのですが、c.sleep ();と打つとエラーになってしまいます。 package pkg6.pkg5.pkg1; import java.util.ArrayList; import java.lang.*; public class Main { public static void main (String[] args) { ArrayList<Character>list2 = new ArrayList<Character>(); list2.add ('A'); list2.add ('b'); for (Character c:list2) { c.sleep (); //↑この部分がエラーになる } } } なぜこうなるのでしょうか?sleepメソッドは呼び出せないのでしょうか? ご教授願います

    • ベストアンサー
    • Java
  • javaについて教えて下さい!

    class SampleA implements Runnable { int tmp1 = 0; int tmp2 = 0; public synchronized void run(){ try{ if(tmp1 == 1){ System.out.print("は"); System.out.print("め"); else if (tmp1 == 2){ System.out.print("じ"); System.out.print("て");          } if(tmp2 == 0) tmp2 = tmp2 + 1; System.out.print("ま"); } System.out.print("。"); }catch(InterrupedException e){ System.out.print("し"); } } } //Sample2.java class Sample2{ public static void main(String[] args){ Sample1 sp = new Sample1(); Thread th1 = new Thread(sp); Thread th2 = new Thread(sp); th1.start(); th2.start(); } } wait(),notify(),throwを使って例外を生じさせるところまではわかったのですが このプログラムを「はじめまして。」と順番に出力するにはどうすればいいですか? プログラム内容は追記する形でお願いします。

    • ベストアンサー
    • Java
  • wait中にinterruptを呼ばれたスレッドの振る舞いについて

    はじめまして。javaの初心者です。 どうしてもわからないことがあります。 例えば、以下のコードで、 class Foo { public synchronized void foo() { while(条件) { try { this.wait(); } catch(InterruptedException ex)    {     処理    } } } } wait中であったスレッドは、他のスレッドによって interrupt()メソッドを実行されたときは、Fooの ロックを取得してからcatch節を実行すると思うのですが、もし、以下のコードの場合は、 class Foo { public synchronized void foo() throws InterruptedException { while(条件) { this.wait(); } } } wait中であったスレッドは、他のスレッドによって interrupt()を実行されたときにすぐにInterrupted Exceptionをスローするのか、それとも Fooクラスのロックを取得してからInterruptedExceptionをスローするのかどちらなのか がわかりません。どうか教えてください。よろしく お願いします。

    • ベストアンサー
    • Java
  • コレクションクラスについて

    ●下記のコードについて質問があります import java.util.*; public class Test { public static void main(String args[]) { ArrayList<ObjectOne> list = new ArrayList<ObjectOne>(); list.add(new ObjectOne()); list.add(new ObjectOne()); list.add(new ObjectOne()); Collections.sort(list); } } class ObjectOne { private int x = 0; private int y = 0; } このソースをコンパイルすると、 シンボル: メソッド sort(java.util.ArrayList<ObjectOne>) 場所 : java.util.Collections の クラス Collections.sort(list); と、エラーが表示されてしまいます。 java.util.*をインポートしているので、上記のようなエラーはでないと 思うのですが、うまくいかないです。おそらく、ObjectOneクラスで 何か処理漏れが起きているのかもしれませんが、エラーとなる原因を 特定することができません。 エラーとなる原因と解消する手立てを教えていただければと思っております。 宜しくお願い致します。 「追記」 ArrayList<ObjectOne> list = new ArrayList<ObjectOne>(); の<ObjectOne>を消せばエラーはなくなりますが、 <ObjectOne>を消さない方針で考えがあればと思っております。

  • VectorクラスとRunnableインターフェース

    こんにちわ。 Java初心者です。 Vector クラスとrunnable インタフェースを実装したスレッドを用いたプログラムを作成中で、自分でも中身が分からなくなってきたうえにMainメソッドの位置が分からなくなってしまいました; 名前の表示が実行できるようにどなたか修正をお願いいたします。 現段階のソースは以下です。 import java.util.*; public class NameList implements Runnable{ private Thread t = null; private Vector list = new Vector(); public void Name(){ List<String> list = new ArrayList<String>(); list.add("Ayane"); list.add("Akiyo"); list.add("Asami"); list.add("Hanabi"); list.add("Meg"); list.add("Michiru"); list.add("Mika"); list.add("Reina"); } public void run(){ while (true){ System.out.println(list.get(0)); try{ Thread.sleep(1000); } catch (InterruptedException e){ break; } } } public void start(){ if(t == null){ t = new Thread(this); t.start(); } } }

    • ベストアンサー
    • Java

専門家に質問してみよう