Objectのwait、notifyとnotifyAll
17575 ワード
Obectのwait、notifyとnotifyAllはObjectが提供する同期方法で、つまりすべての対象が生まれて持ってくる方法です。javaをやるのはこのいくつかの方法を知らないことはないと思います。彼は一体どうやって使うのですか?ここに自分の理解を記録してください。
まず一番簡単な例を紹介します。
理由については、jdkソースwait方法の説明は以下の通りです。
対象のモニターは何ですか?
簡単に言えば、モニタはjavaオブジェクトが同期動作を実現するための仕組みであり、javapを使って上記の例のスレッド部分の逆コンパイル命令を確認する。
このオブジェクトのモニターはどうやって持ちますか?
jdkソースのnotify方法には3つの方法が挙げられています。
文章の開始例に対するエラーの例を挙げます。
エラーの例1
notify方法はモニタを獲得していません。
エラーの例2:
実行結果:
この例は私の最初の書き方です。ここに置くのはちょっと合わないです。彼はwaitとnotifyの間違いではなく、Runnableを間違えて使ったためです。結局は置くことにします。誰かがすぐに分かりにくくなるのを防ぐためです。
例ではnew Runnableを用いて匿名の内部クラスを作成し、構造パラメータとしてnew Threadに伝達し、構造の対象mtと匿名の内部類のthisは同一のオブジェクトではない。なので、notifyが機能しなくなりました。
まず一番簡単な例を紹介します。
1 public class SynchronizedTest {
2 public static void main(String[] args) throws Exception {
3 Thread mt = new Thread(){
4 @Override
5 public void run() {
6 synchronized (this) {
7 System.out.println(" ");
8 try {
9 this.wait();
10 } catch (InterruptedException e) {
11 e.printStackTrace();
12 }
13 System.out.println(" ");
14 }
15 }
16 };
17 mt.start();
18 Thread.sleep(500);
19 synchronized (mt) {
20 mt.notify();
21 }
22 }
23 }
実行結果:
上記の例では、waitとnotify方法は、いずれもsynchronizedコード体で実行されていますが、synchronizedで修飾されていない場合は、直接使用するとjava.lang.IllagalMonitontacteExceptionが例外となります。理由については、jdkソースwait方法の説明は以下の通りです。
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
* either through a call to the {@code notify} method or the
* {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
翻訳してください: 。 , notify , notifyAll 。 。
まとめてみますと、waitなどの一連の方法を使うには、現在の対象のモニター(多くのところではモニタロックと呼ばれています)が必要です。対象のモニターは何ですか?
簡単に言えば、モニタはjavaオブジェクトが同期動作を実現するための仕組みであり、javapを使って上記の例のスレッド部分の逆コンパイル命令を確認する。
1 final class com.xxx.SynchronizedTest$1 extends java.lang.Thread {
2 com.xxx.SynchronizedTest$1();
3 Code:
4 0: aload_0
5 1: invokespecial #1 // Method java/lang/Thread."":()V
6 4: return
7
8 public void run();
9 Code:
10 0: aload_0
11 1: dup
12 2: astore_1
13 3: monitorenter
14 4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
15 7: ldc #3 // String
16 9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
17 12: aload_0
18 13: invokevirtual #5 // Method java/lang/Object.wait:()V
19 16: goto 24
20 19: astore_2
21 20: aload_2
22 21: invokevirtual #7 // Method java/lang/InterruptedException.printStackTrace:()V
23 24: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
24 27: ldc #8 // String
25 29: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
26 32: aload_1
27 33: monitorexit
28 34: goto 42
29 37: astore_3
30 38: aload_1
31 39: monitorexit
32 40: aload_3
33 41: athrow
34 42: return
35 Exception table:
36 from to target type
37 12 16 19 Class java/lang/InterruptedException
38 4 34 37 any
39 37 40 37 any
40 }
13行と27行はmonitonterとmonit orexitの二つのコマンドを見ることができます。monitonterはmonitorを獲得しようと試みています。成功すれば実行し、成功しなければブロッキングします。monitoshotはリリースmonitorです。このオブジェクトのモニターはどうやって持ちますか?
jdkソースのnotify方法には3つの方法が挙げられています。
By executing a synchronized instance method of that object.
By executing the body of a {@code synchronized} statement
that synchronizes on the object.
For objects of type {@code Class,} by executing a
synchronized static method of that class.
翻訳は大体 。
synchronized 。
Class , 。
私が理解しているのは、synchronizedによって修正された方法またはコードブロック(コードブロックであれば、同じ対象をsynchronizedのパラメータとして使用する必要があります。目的は同じモニタを取得するためです。)上の逆コンパイルスレッドの匿名内部コマンドに合わせて、synchronizedを使って修正されたコードはモニタを取得するために、waitとnotifyはモニタを獲得しなければなりません。文章の開始例に対するエラーの例を挙げます。
エラーの例1
1 public class SynchronizedTest {
2 public static void main(String[] args) throws Exception {
3 Thread mt = new Thread(){
4 @Override
5 public void run() {
6 synchronized (this) {
7 System.out.println(" ");
8 try {
9 this.wait();
10 } catch (InterruptedException e) {
11 e.printStackTrace();
12 }
13 System.out.println(" ");
14 }
15 }
16 };
17 mt.start();
18 Thread.sleep(500);
19 // synchronized (mt) {
20 mt.notify();
21 // }
22 }
23 }
実行結果:
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at xxx
理由:notify方法はモニタを獲得していません。
エラーの例2:
1 public class SynchronizedTest {
2 public static void main(String[] args) throws Exception {
3 Thread mt = new Thread(new Runnable() {
4 @Override
5 public void run() {
6 synchronized (this) {
7 System.out.println(" ");
8 try {
9 this.wait();
10 } catch (InterruptedException e) {
11 e.printStackTrace();
12 }
13 System.out.println(" ");
14 }
15 }
16 });
17 mt.start();
18 Thread.sleep(500);
19 synchronized (mt) {
20 mt.notify();
21 }
22 }
23 }
実行結果:
( wait 。。。
理由:この例は私の最初の書き方です。ここに置くのはちょっと合わないです。彼はwaitとnotifyの間違いではなく、Runnableを間違えて使ったためです。結局は置くことにします。誰かがすぐに分かりにくくなるのを防ぐためです。
例ではnew Runnableを用いて匿名の内部クラスを作成し、構造パラメータとしてnew Threadに伝達し、構造の対象mtと匿名の内部類のthisは同一のオブジェクトではない。なので、notifyが機能しなくなりました。