Javaでデッドロックを引き起こすプログラムとその修正方法


デッドロックを引き起こすプログラムの例:

public class DeadlockExample {
    private static Object resource1 = new Object();
    private static Object resource2 = new Object();
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("スレッド1: resource1をロックしました");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resource2) {
                    System.out.println("スレッド1: resource2をロックしました");
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("スレッド2: resource2をロックしました");
                synchronized (resource1) {
                    System.out.println("スレッド2: resource1をロックしました");
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

上記のプログラムでは、2つのスレッドがそれぞれresource1resource2をロックしようとしていますが、順番が逆になっています。つまり、スレッド1がresource1をロックし、スレッド2がresource2をロックしようとすると同時に、スレッド2がresource1を待っています。同様に、スレッド1もresource2を待っています。これにより、デッドロックが発生します。

デッドロックの修正方法:

デッドロックを解決するためには、以下のようなアプローチを取ることができます。

  1. リソースの順序付け: スレッドが複数のリソースを要求する場合、リソースに対する一貫した順序付けを行います。つまり、スレッドは常に同じ順序でリソースを要求するようにします。

  2. リソースのタイムアウト: スレッドが一定時間内に必要なリソースを取得できない場合、リソースを解放して他のスレッドが使用できるようにします。

  3. スレッドの割り込み: スレッドがデッドロックに陥った場合、他のスレッドから割り込みを行い、デッドロックから脱出できるようにします。

修正されたプログラムの例:

public class DeadlockFixExample {
    private static Object resource1 = new Object();
    private static Object resource2 = new Object();
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("スレッド1: resource1をロックしました");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resource2) {
                    System.out.println("スレッド1: resource2をロックしました");
                }
            }
        });
        Thread thread2 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("スレッド2: resource1をロックしました");
                synchronized (resource2) {
                    System.out.println("スレッド2: resource2をロックしました");
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

修正後のプログラムでは、スレッド1とスレッド2がリソースのロックを要求する順序が一致しています。スレッド1はresource1をロックし、スレッド2も同じ順序でresource1をロックするように修正しました。

また、デッドロックを回避するために、スレッドが一定時間内に必要なリソースを取得できない場合にタイムアウトを設定することも考えられます。これにはtryLock()メソッドやLockインターフェースを使用する方法があります。

デッドロックの修正方法は状況によって異なる場合があります。デッドロックを回避するための最適な方法は、アプリケーションの要件やコードの特性によって異なる場合があります。