Javaのデッドロック動力ノードJava学院の整理
デッドロックは、少なくとも2つのスレッドと2つ以上のリソースを伴う2つのスレッドが永久的にブロックされる場合の動作状態である。ここで簡単なプログラムを書きました。デッドロックプログラムを引き起こします。その後、どうやってそれを分析するかが分かります。
Javaデッドロックの例
ThreadDeadlock.java
メイン関数では、同期スレッドとして動作するスレッドを3つ使用し、各スレッドに共有可能なリソースがあります。
これらのスレッドは最初のオブジェクトに対して封鎖を取得するように動作します。しかし、2番目の対象のようにブロックを取ってみると、待ち状態になります。他のスレッドによって封鎖されています。このようにスレッドがデッドロックを引き起こす過程で,資源に依存するサイクルが形成される。
上のプログラムを実行すると、出力が発生しますが、プログラムはロックで停止できません。
t 1 acquiring lock on java.lang.Object@6d9dd520
t 1 acquired lock on java.lang.Object@6d9dd520
t 2 acquiring lock on java.lang.Object@22aed3a5
t 2 acquired lock on java.lang.Object@22aed3a5
t 3 acquiring lock on java.lang.Object@218c2661
t 3 acquired lock on java.lang.Object@218c2661
t 1 acquiring lock on java.lang.Object@22aed3a5
t 2 acquiring lock on java.lang.Object@218c2661
t 3 acquiring lock on java.lang.Object@6d9dd520
ここでははっきりとアウトプットの結果からデッドロック状態が分かりますが、私たちが実際に生活しているアプリケーションでデッドロックを発見し、それを排除するのは非常に難しいです。
デッドロックを分析する
デッドロックを分析するためには、デッドロック状態のスレッドに注目して、資源は再び封鎖されるのを待つ必要があります。各リソースには独特のIDがあります。このIDを持っていると、どのプロセスが対象を封鎖したのかが分かります。例えば、スレッド「t 3」は、0 x 00013 df 2 f 658を封鎖するのを待っているが、スレッド「t 1」によって封鎖されている。
デッドロック環境を分析すると、スレッドがデッドロックを起こしていることがわかったら、コードを変えてデッドロックが発生しないようにします。
デッドロックを避ける
多くの方針があります。デッドロックを回避するために私達に使ってもいいです。
ネスト封鎖を避ける:これはデッドロックの最も主要な原因であり、もしあなたがすでに資源を持っているなら、もう他の資源を封鎖しないでください。もしあなたが運転している時に相手が一つしか封鎖されていないと、ロック状態はほとんど現れません。例えば、ここはもう一つの運転中に封鎖されていないrun()の方法であり、プログラムの運行にはデッドロックがなく、成功しました。
無期限待ちを避ける:もし2つのスレッドが対象の終了を待っているなら、無期限でスレッドを使って参加し、もしあなたのスレッドが別のスレッドの終了を待つ必要があるなら、プロセスの終了を待って参加したら一番長い時間準備してください。
Javaデッドロックの例
ThreadDeadlock.java
package com.bjpowernode.threads;
public class ThreadDeadlock {
public static void main(String[] args) throws InterruptedException {
Object obj1 = new Object();
Object obj2 = new Object();
Object obj3 = new Object();
Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1");
Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2");
Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3");
t1.start();
Thread.sleep(5000);
t2.start();
Thread.sleep(5000);
t3.start();
}
}
class SyncThread implements Runnable{
private Object obj1;
private Object obj2;
public SyncThread(Object o1, Object o2){
this.obj1=o1;
this.obj2=o2;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + " acquiring lock on "+obj1);
synchronized (obj1) {
System.out.println(name + " acquired lock on "+obj1);
work();
System.out.println(name + " acquiring lock on "+obj2);
synchronized (obj2) {
System.out.println(name + " acquired lock on "+obj2);
work();
}
System.out.println(name + " released lock on "+obj2);
}
System.out.println(name + " released lock on "+obj1);
System.out.println(name + " finished execution.");
}
private void work() {
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
上のプログラムで同期スレッドがRunnableのインターフェースを完了しています。この二つのオブジェクトは相手にデッドロックを求め、同期ブロッキングを使っています。メイン関数では、同期スレッドとして動作するスレッドを3つ使用し、各スレッドに共有可能なリソースがあります。
これらのスレッドは最初のオブジェクトに対して封鎖を取得するように動作します。しかし、2番目の対象のようにブロックを取ってみると、待ち状態になります。他のスレッドによって封鎖されています。このようにスレッドがデッドロックを引き起こす過程で,資源に依存するサイクルが形成される。
上のプログラムを実行すると、出力が発生しますが、プログラムはロックで停止できません。
t 1 acquiring lock on java.lang.Object@6d9dd520
t 1 acquired lock on java.lang.Object@6d9dd520
t 2 acquiring lock on java.lang.Object@22aed3a5
t 2 acquired lock on java.lang.Object@22aed3a5
t 3 acquiring lock on java.lang.Object@218c2661
t 3 acquired lock on java.lang.Object@218c2661
t 1 acquiring lock on java.lang.Object@22aed3a5
t 2 acquiring lock on java.lang.Object@218c2661
t 3 acquiring lock on java.lang.Object@6d9dd520
ここでははっきりとアウトプットの結果からデッドロック状態が分かりますが、私たちが実際に生活しているアプリケーションでデッドロックを発見し、それを排除するのは非常に難しいです。
デッドロックを分析する
2012-12-27 19:08:34
Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.5-b02 mixed mode):
"Attach Listener" daemon prio=5 tid=0x00007fb0a2814000 nid=0x4007 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" prio=5 tid=0x00007fb0a2801000 nid=0x1703 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"t3" prio=5 tid=0x00007fb0a204b000 nid=0x4d07 waiting for monitor entry [0x000000015d971000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
- waiting to lock <0x000000013df2f658> (a java.lang.Object)
- locked <0x000000013df2f678> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:722)
"t2" prio=5 tid=0x00007fb0a1073000 nid=0x4207 waiting for monitor entry [0x000000015d209000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
- waiting to lock <0x000000013df2f678> (a java.lang.Object)
- locked <0x000000013df2f668> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:722)
"t1" prio=5 tid=0x00007fb0a1072000 nid=0x5503 waiting for monitor entry [0x000000015d86e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
- waiting to lock <0x000000013df2f668> (a java.lang.Object)
- locked <0x000000013df2f658> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:722)
"Service Thread" daemon prio=5 tid=0x00007fb0a1038000 nid=0x5303 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" daemon prio=5 tid=0x00007fb0a1037000 nid=0x5203 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" daemon prio=5 tid=0x00007fb0a1016000 nid=0x5103 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" daemon prio=5 tid=0x00007fb0a4003000 nid=0x5003 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=5 tid=0x00007fb0a4800000 nid=0x3f03 in Object.wait() [0x000000015d0c0000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177)
"Reference Handler" daemon prio=5 tid=0x00007fb0a4002000 nid=0x3e03 in Object.wait() [0x000000015cfbd000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000013de75320> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
- locked <0x000000013de75320> (a java.lang.ref.Reference$Lock)
"VM Thread" prio=5 tid=0x00007fb0a2049800 nid=0x3d03 runnable
"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007fb0a300d800 nid=0x3503 runnable
"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007fb0a2001800 nid=0x3603 runnable
"GC task thread#2 (ParallelGC)" prio=5 tid=0x00007fb0a2003800 nid=0x3703 runnable
"GC task thread#3 (ParallelGC)" prio=5 tid=0x00007fb0a2004000 nid=0x3803 runnable
"GC task thread#4 (ParallelGC)" prio=5 tid=0x00007fb0a2005000 nid=0x3903 runnable
"GC task thread#5 (ParallelGC)" prio=5 tid=0x00007fb0a2005800 nid=0x3a03 runnable
"GC task thread#6 (ParallelGC)" prio=5 tid=0x00007fb0a2006000 nid=0x3b03 runnable
"GC task thread#7 (ParallelGC)" prio=5 tid=0x00007fb0a2006800 nid=0x3c03 runnable
"VM Periodic Task Thread" prio=5 tid=0x00007fb0a1015000 nid=0x5403 waiting on condition
JNI global references: 114
Found one Java-level deadlock:
=============================
"t3":
waiting to lock monitor 0x00007fb0a1074b08 (object 0x000000013df2f658, a java.lang.Object),
which is held by "t1"
"t1":
waiting to lock monitor 0x00007fb0a1010f08 (object 0x000000013df2f668, a java.lang.Object),
which is held by "t2"
"t2":
waiting to lock monitor 0x00007fb0a1012360 (object 0x000000013df2f678, a java.lang.Object),
which is held by "t3"
Java stack information for the threads listed above:
===================================================
"t3":
at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
- waiting to lock <0x000000013df2f658> (a java.lang.Object)
- locked <0x000000013df2f678> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:722)
"t1":
at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
- waiting to lock <0x000000013df2f668> (a java.lang.Object)
- locked <0x000000013df2f658> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:722)
"t2":
at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
- waiting to lock <0x000000013df2f678> (a java.lang.Object)
- locked <0x000000013df2f668> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:722)
Found 1 deadlock.
これらの3つのスレッドの回転出力は,デッドロック環境とスレッド,およびデッドロック環境を含むリソースを明確に説明した。デッドロックを分析するためには、デッドロック状態のスレッドに注目して、資源は再び封鎖されるのを待つ必要があります。各リソースには独特のIDがあります。このIDを持っていると、どのプロセスが対象を封鎖したのかが分かります。例えば、スレッド「t 3」は、0 x 00013 df 2 f 658を封鎖するのを待っているが、スレッド「t 1」によって封鎖されている。
デッドロック環境を分析すると、スレッドがデッドロックを起こしていることがわかったら、コードを変えてデッドロックが発生しないようにします。
デッドロックを避ける
多くの方針があります。デッドロックを回避するために私達に使ってもいいです。
ネスト封鎖を避ける:これはデッドロックの最も主要な原因であり、もしあなたがすでに資源を持っているなら、もう他の資源を封鎖しないでください。もしあなたが運転している時に相手が一つしか封鎖されていないと、ロック状態はほとんど現れません。例えば、ここはもう一つの運転中に封鎖されていないrun()の方法であり、プログラムの運行にはデッドロックがなく、成功しました。
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + " acquiring lock on " + obj1);
synchronized (obj1) {
System.out.println(name + " acquired lock on " + obj1);
work();
}
System.out.println(name + " released lock on " + obj1);
System.out.println(name + " acquiring lock on " + obj2);
synchronized (obj2) {
System.out.println(name + " acquired lock on " + obj2);
work();
}
System.out.println(name + " released lock on " + obj2);
System.out.println(name + " finished execution.");
}
要求されたものだけを封鎖します。例えば上記の手順で私が封鎖している完全な対象の資源だけを利用してください。しかし、私たちが所属分野の一つだけに興味を持つなら、その特殊な分野を完全な対象ではなく封じ込めなければなりません。無期限待ちを避ける:もし2つのスレッドが対象の終了を待っているなら、無期限でスレッドを使って参加し、もしあなたのスレッドが別のスレッドの終了を待つ必要があるなら、プロセスの終了を待って参加したら一番長い時間準備してください。