1、Javaオブジェクトロック
Javaのすべてのオブジェクトには、固有のロックがあります。いつでも、最大で1つのスレッドがロックを所有できます。ロックの概念は、synchronizedキーワードを使用するときに役立ちます。
2.デッドロックの例
以下は、デッドロックが発生しやすいサンプル参照コードです。オンラインIDEを使用して上記のプログラムを実行することはお勧めしません。javacコマンドを使用してコンパイルしてから、javaコマンドを使用して実行することをお勧めします。
// Java program to illustrate Deadlock
// in multithreading.
class Util
{
// Util class to sleep a thread
static void sleep(long millis)
{
try
{
Thread.sleep(millis);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
// This class is shared by both threads
class Shared
{
// first synchronized method
synchronized void test1(Shared s2)
{
System.out.println("test1-begin");
Util.sleep(1000);
// taking object lock of s2 enters
// into test2 method
s2.test2();
System.out.println("test1-end");
}
// second synchronized method
synchronized void test2()
{
System.out.println("test2-begin");
Util.sleep(1000);
// taking object lock of s1 enters
// into test1 method
System.out.println("test2-end");
}
}
class Thread1 extends Thread
{
private Shared s1;
private Shared s2;
// constructor to initialize fields
public Thread1(Shared s1, Shared s2)
{
this.s1 = s1;
this.s2 = s2;
}
// run method to start a thread
@Override
public void run()
{
// taking object lock of s1 enters
// into test1 method
s1.test1(s2);
}
}
class Thread2 extends Thread
{
private Shared s1;
private Shared s2;
// constructor to initialize fields
public Thread2(Shared s1, Shared s2)
{
this.s1 = s1;
this.s2 = s2;
}
// run method to start a thread
@Override
public void run()
{
// taking object lock of s2
// enters into test2 method
s2.test1(s1);
}
}
public class Deadlock
{
public static void main(String[] args)
{
// creating one object
Shared s1 = new Shared();
// creating second object
Shared s2 = new Shared();
// creating first thread and starting it
Thread1 t1 = new Thread1(s1, s2);
t1.start();
// creating second thread and starting it
Thread2 t2 = new Thread2(s1, s2);
t2.start();
// sleeping main thread
Util.sleep(2000);
}
}
上記のコードは何をしますか?
- スレッドt1が開始し、s1のオブジェクトロックを取得してtest1メソッドを呼び出します。
- スレッドt2が開始し、s2のオブジェクトロックを取得してtest1メソッドを呼び出します。
- t1はtest1-beginを出力し、t2はtest-2 beginを出力し、両方とも1秒間待機するため、どちらかのスレッドが開始しない場合は、両方のスレッドを開始できます。
- t1はs2のオブジェクトロックを取得しようとし、test2メソッドを呼び出しますが、t2によってすでに取得されているため、解放されるまで待機します。s2のロックを取得するまで、s1のロックは解除されません。
- 同じことがt2にも当てはまります。s1のオブジェクトロックを取得しようとし、メソッドtest1を呼び出しますが、t1によってすでに取得されているため、t1がロックを解放するまで待機する必要があります。t2は、s1のロックを取得するまで、s2のロックも解放しません。
- これで、両方のスレッドが待機状態になり、もう一方のスレッドがロックを解放するのを待機します。誰が最初にロックを解除するかという競合状態が発生しました。
- それらのいずれもロックを解放する準備ができていないため、これはデッドロック状態です。
- プログラムを実行すると、実行が中断されているように見えます。
3.デッドロックを表示する
前のセクションのコードを実行した後、デッドロックが発生したときにウィンドウの詳細を入力すると、2つのJavaスレッドが実行されていることがわかります。
別のcmdウィンドウを開始して、jcmd $ PID Thread.printコマンドを実行できます。ここで、$PIDは上の図のスレッドIDです。
ここにjcmd53364Thread.printと入力し、Enterキーを押すと、デッドロックが見つかったことを明確に示す出力が表示されます。
4.デッドロックを回避します
ネストされたロックの回避:これがデッドロックの主な原因です。デッドロックは主に、複数のスレッドをロックするときに発生します。1つのスレッドを指定した場合は、複数のスレッドをロックしないでください。
不要なロックは避けてください。必要なメンバーのみをロックする必要があります。不必要なロックはデッドロックにつながる可能性があります。
結合の使用:デッドロック状態は、あるスレッドが別のスレッドの終了を待機しているときに発生します。これが発生した場合は、Thread.joinと、実行にかかると思われる最大時間を使用できます。