https://blogs.oracle.com/geertjan/entry/lock_contention_profiling_in_netbeans
ロック競合のプロファイリングは、マルチコア環境において非常に重要です。スレッドが別のスレッドが保持しているロックを取ろうとすると、ロック解除を待たざるを得ないため、ロック競合が発生します。ロックを使うことによってシーケンシャルな作業になるため、ロック競合により、並列に複数のコアを使用して得られる可能性のあるパフォーマンス向上を削がれます。最悪のシナリオでは、1つのスレッドしか動作しません。NetBeans 8には、NetBeans Profilerで新たにロックの競合、つまり、パフォーマンスのボトルネックに関する詳細情報を表示するようになりました。これを使うことで、問題のスレッドを識別しやすくなります。
例として、Java Tutorialにあるデッドロックのサンプルコードを使い、NetBeans IDEのツールでコードを識別・分析してみましょう。
Deadlock (The JavaTM Tutorials)このデッドロックの表現がいいですね。
http://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html
AlphonseとGastonは友人同士で、礼儀正しい者たちです。礼儀の厳格なルールは、友人にお辞儀をするときは、相手がお辞儀を返すタイミングがあるまで、ずっと誰が初めにお辞儀をしたか、または少なくともお辞儀の順序を識別するために、ファイルを右クリックし、[ファイルをプロファイル(Profile file)]を選択します。Profile Task Managerで、以下のように選択します。
お辞儀をし続けなければならない、というものです。残念ながら、このルールは2人が同時にお互いに屈するかもしれないという可能性を考慮していません。

[実行]をクリックすると、スレッドウィンドウが開き、2個のスレッドがブロックされている、つまり、同期メソッドに入ったり、ブロックしようとしている関連するスレッドがブロックされていることを赤い「モニター」の線が知らせてくれています。

では、どちらのスレッドがロックを保持しているのでしょう?どちらがどちらにブロックされているのでしょう?上記の画面ではわかりませんね。
NetBeans 8の新機能で、新しいロック競合ウィンドウを使い、デッドロックを分析し、どちらのスレッドがロックしているのかを究明することができるようになっています。
(訳注)
NetBeansの日本語UIでは「ロック内容」になっています。
以下はロックをシミュレートしたコードですが、ちょっとだけ手を入れています。それは、setNameメソッドをスレッドで使い、関連するNetBeansのツールで分析しやすくしています。また、匿名inner RunnablesをLambda式に変換しています。
上記のコードでは、bowBack(お辞儀を返す)を起動しようとすると、両方のスレッドがブロックする可能性が非常に高いのです。各スレッドは他方のお辞儀が終了するのを待っているため、どちらのブロックも終了しません。
package
org.demo;
public
class
Deadlock {
static
class
Friend {
private
final
String name;
public
Friend(String name) {
this
.name = name;
}
public
String getName() {
return
this
.name;
}
public
synchronized
void
bow(Friend bower) {
System.out.format(
"%s: %s"
+
" has bowed to me!%n"
,
this
.name, bower.getName());
bower.bowBack(
this
);
}
public
synchronized
void
bowBack(Friend bower) {
System.out.format(
"%s: %s"
+
" has bowed back to me!%n"
,
this
.name, bower.getName());
}
}
public
static
void
main(String[] args) {
final
Friend alphonse
=
new
Friend(
"Alphonse"
);
final
Friend gaston
=
new
Friend(
"Gaston"
);
Thread t1 =
new
Thread(() -> {
alphonse.bow(gaston);
});
t1.setName(
"Alphonse bows to Gaston"
);
t1.start();
Thread t2 =
new
Thread(() -> {
gaston.bow(alphonse);
});
t2.setName(
"Gaston bows to Alphonse"
);
t2.start();
}
}
[注意]ご覧になっておわかりかと思いますが、コードでThreadを作成しているところであればどこでも、ThreadのsetNameメソッドを使って名前を付けておくべきでしょう。というのは、スレッドに名前を付けると、IDEのツールでより多くの意味を持つようになるからです。もし付けなかったとしたら、スレッドの順序をベースとした"thread-5"とか"thread-6"といった、無意味なスレッド名を使います。 (通常は、上記のような簡単なデモのシナリオ以外では、同じクラスでスレッドを開始しません。そのため、スレッドの開始順序を知らないので、Thread-5やThread-6が何を意味するのか全く見当がつかないことでしょう)。
もう少しコンパクトにすると、以下のような感じになります。
Thread t1 =
new
Thread(() -> {
alphonse.bow(gaston);
},
"Alphonse bows to Gaston"
);
t1.start();
Thread t2 =
new
Thread(() -> {
gaston.bow(alphonse);
},
"Gaston bows to Alphonse"
);
t2.start();
0 件のコメント:
コメントを投稿