質問者さんのコードが意図通り動作しないのは、wait
とnofityAll
でモニターしている(対象としている)オブジェクトがそれぞれ別のスレッドのthis
であるからです。例えばThreadXX2
のs1
メソッド内のwait();
は、冗長に書くとthis.wait()
です。また、notifyAll();
に関してもthis.nofityAll()
で、自スレッドの待機を解除できるのは自スレッドのみであり、これがThreadXX2
とThreadYY2
それぞれに言えているので相互に対話していることにならず、停止したままになっています。振り返って考えてみてください。質問者さんの元のコードではそれぞれのスレッドでwait
、nofifyAll
を実行していますが、コード上で関連しているようには見えないと思いませんか?
様々なやり方はあると思いますが、ある値がtrue
であるときにThreadXX2
が動き、またそのある値がfalse
であるときにThreadYY2
が動くようにする、と言うのであれば、そのある値を同期用に共有するオブジェクトにします。尚、質問者さんのオリジナルのコードではその共有値はpublic static boolean n;
で扱っていますが、マルチスレッドでの運用では値の更新と読み取り時にガードが甘いものとなり、一貫性が保てなくなります。(今のコードではほとんど問題になりませんが)
そんなことを踏まえた上で修正してみたものが以下のコードです。自作のSignal
クラスのインスタンスを共通に参照するオブジェクトとしてtrue
のときはThreadXX2
、false
のときはThreadYY2
が待機を解除して実行を続けるようになっています。synchronized
キーワードを指定している位置が変わっているのにも注意してみてみてください。
Java
1 class Signal {
2 private boolean value ;
3
4 public Signal ( boolean initial ) {
5 this . value = initial ;
6 }
7
8 // 指定のboolean値になるまで待機
9 public synchronized void waitFor ( boolean value ) throws InterruptedException {
10 while ( this . value != value ) {
11 this . wait ( ) ;
12 }
13 }
14
15 // 指定のboolean値をセットしてnotifyAll(イベント発火)
16 public synchronized void setEvent ( boolean value ) {
17 this . value = value ;
18 this . notifyAll ( ) ;
19 }
20 }
21
22 class ThreadXX2 extends Thread {
23 MessagePrint m = null ;
24 Signal signal = null ;
25
26 public ThreadXX2 ( MessagePrint nm , Signal signal ) {
27 super ( ) ;
28 m = nm ;
29 this . signal = signal ;
30 }
31
32 public void s1 ( ) {
33 try {
34 while ( true ) {
35 System . out . println ( "ThreadXX2.s1(): while1" ) ;
36
37 // trueになるまで待機
38 signal . waitFor ( true ) ;
39 m . message ( "XX" ) ;
40
41 // ThreadYY2のスレッドの待機を解除すべく、falseにセット。
42 signal . setEvent ( false ) ;
43
44 Thread . sleep ( 500 ) ;
45 }
46 } catch ( Exception e ) {
47 System . out . println ( "ThreadXX2.s1() - Exception:" + e . getMessage ( ) ) ;
48 }
49 }
50
51 public void run ( ) {
52 s1 ( ) ;
53 }
54 }
55
56 class ThreadYY2 extends Thread {
57 MessagePrint m = null ;
58 Signal signal = null ;
59
60 public ThreadYY2 ( MessagePrint nm , Signal signal ) {
61 super ( ) ;
62 m = nm ;
63 this . signal = signal ;
64 }
65
66 public void s2 ( ) {
67 try {
68 while ( true ) {
69 System . out . println ( "ThreadYY2.s2(): while1" ) ;
70
71 // falseになるまで待機
72 signal . waitFor ( false ) ;
73 m . message ( "YY" ) ;
74
75 // ThreadXX2のスレッドの待機を解除すべく、trueにセット。
76 signal . setEvent ( true ) ;
77
78 // 視認し易くする為500ミリ秒待機
79 Thread . sleep ( 500 ) ;
80 }
81 } catch ( Exception e ) {
82 System . out . println ( "ThreadYY2.s2() - Exception:" + e . getMessage ( ) ) ;
83 }
84 }
85
86 public void run ( ) {
87 s2 ( ) ;
88 }
89 }
90
91 class MessagePrint {
92 public void message ( String text ) {
93 System . out . println ( text ) ;
94 }
95 }
96
97 public class ThreadEx2 {
98 public static void main ( String [ ] args ) {
99
100 // 共有のシグナル(イベント)オブジェクトを生成
101 Signal signal = new Signal ( true ) ;
102 final MessagePrint m = new MessagePrint ( ) ;
103 new ThreadXX2 ( m , signal ) . start ( ) ;
104 new ThreadYY2 ( m , signal ) . start ( ) ;
105 }
106 }
これを実行すると端末上において以下のような出力が得られます。Javaのバージョンは、OpenJDKの 1.8.0_252 です。
sh
1 $ javac ThreadEx2.java
2 $ java ThreadEx2
3 ThreadXX2.s1(): while1
4 XX
5 ThreadYY2.s2(): while1
6 YY
7 ThreadXX2.s1(): while1
8 XX
9 ThreadYY2.s2(): while1
10 YY
11 ThreadXX2.s1(): while1
12 XX
13 ThreadYY2.s2(): while1
14 YY
15 ThreadXX2.s1(): while1
16 XX
17 ThreadYY2.s2(): while1
18 YY
19 ThreadXX2.s1(): while1
20 XX
21 ThreadYY2.s2(): while1
22 YY
23 ThreadXX2.s1(): while1
24 XX
25 ThreadYY2.s2(): while1
26 YY
27 ...ずっと続く
例をもうひとつ挙げます。wait
とnotifyAll(含むnotify)
のメソッドはその対象のオブジェクトが合致していればいいわけなので、上記のように共通のオブジェクトを使うのではなく、ThreadXX2
とThreadYY2
のそれぞれのインスタンスを直接指定することでも相互に待機、解除させることができます。コードの例としては分かりづらい気はするので、参考程度に読んでいただくのが良いかもしれません。これもsynchroized
キーワードの位置に注意してみてください。
Java
1 class ThreadXX2 extends Thread {
2 MessagePrint m = null ;
3 ThreadYY2 yy2 = null ;
4
5 public ThreadXX2 ( MessagePrint nm ) {
6 super ( ) ;
7 m = nm ;
8 }
9
10 public void setThreadYY2 ( ThreadYY2 yy2 ) {
11 this . yy2 = yy2 ;
12 }
13
14 public void s1 ( ) {
15 while ( true ) {
16 System . out . println ( "ThreadXX2.s1(): while1" ) ;
17 m . message ( "XX" ) ;
18 synchronized ( yy2 ) {
19 yy2 . notifyAll ( ) ;
20 }
21
22 try {
23 synchronized ( this ) {
24 wait ( ) ;
25 }
26
27 Thread . sleep ( 500 ) ;
28 } catch ( Exception e ) {
29 System . out . println ( "ThreadXX2.s1(): Exception " + e . getMessage ( ) ) ;
30 }
31 }
32 }
33
34 public void run ( ) {
35 s1 ( ) ;
36 }
37 }
38
39 class ThreadYY2 extends Thread {
40 MessagePrint m = null ;
41 ThreadXX2 xx2 = null ;
42
43 public ThreadYY2 ( MessagePrint nm ) {
44 super ( ) ;
45 m = nm ;
46 }
47
48 public void setThreadXX2 ( ThreadXX2 xx2 ) {
49 this . xx2 = xx2 ;
50 }
51
52 public void s2 ( ) {
53 while ( true ) {
54 System . out . println ( "ThreadYY2.s2(): while1" ) ;
55 m . message ( "YY" ) ;
56 synchronized ( xx2 ) {
57 xx2 . notifyAll ( ) ;
58 }
59
60 try {
61 synchronized ( this ) {
62 wait ( ) ;
63 }
64
65 Thread . sleep ( 500 ) ;
66 } catch ( Exception e ) {
67 System . out . println ( "ThreadYY2.s2(): Exception " + e . getMessage ( ) ) ;
68 }
69 }
70 }
71
72 public void run ( ) {
73 s2 ( ) ;
74 }
75 }
76
77 class MessagePrint {
78 public void message ( String text ) {
79 System . out . println ( text ) ;
80 }
81 }
82
83 public class ThreadEx2 {
84 public static void main ( String [ ] args ) {
85 final MessagePrint m = new MessagePrint ( ) ;
86 ThreadXX2 xx2 = new ThreadXX2 ( m ) ;
87 ThreadYY2 yy2 = new ThreadYY2 ( m ) ;
88
89 xx2 . setThreadYY2 ( yy2 ) ;
90 yy2 . setThreadXX2 ( xx2 ) ;
91
92 xx2 . start ( ) ;
93 yy2 . start ( ) ;
94
95 /*
96 xx2.join();
97 yy2.join();
98 */
99 }
100 }