【Javaマルチスレッド】デッドロック問題

デッドロック

デッドロックの理解: 異なるスレッドが相手が必要とする同期リソースを諦めずに占有し、相手が必要な同期リソースを放棄するのを待っているため、スレッドデッドロックが形成されます。

説明:

1. デッドロックが発生すると、例外やプロンプトは表示されませんが、すべてのスレッドがブロックされ、続行できなくなります。

2. 同期を使用する場合は、デッドロックを回避する必要があります

解決

特殊なアルゴリズムと原則

同期リソースの定義を最小限に抑える

ネストされた同期を避けるようにしてください

================================================= =======================

まず最初に、JDK の Thread.State クラスによって定義されるスレッドの状態を理解するために 2 つの図を見てみましょう。

マルチスレッドを実装するには、メイン スレッドで新しいスレッド オブジェクトを作成する必要があります。Java 言語は、Thread クラスとそのサブクラスのオブジェクトを使用してスレッドを表し、通常、ライフサイクル全体で次の 5 つの状態を経ます。

New : Thread クラスまたはそのサブクラスのオブジェクトが宣言および作成されると、新しいスレッド オブジェクトは新しい状態になります。

Ready:新しく作成された状態のスレッドが start() された後、スレッド キューに入り、CPU タイム スライスを待ちます。この時点では、実行条件は整っていますが、CPU リソースは割り当てられていません。

実行中:準備完了スレッドがスケジュールされ、CPU リソースを取得すると、実行状態になります。run() メソッドは、スレッドの操作と機能を定義します。

ブロッキング:特定の特殊な状況下で、人為的に中断されたり、入出力操作を実行したりすると、CPU を放棄し、自身の実行を一時的に中断し、ブロック状態になります。

死:スレッドがすべての作業を完了したか、スレッドが早期に強制的に終了したか、例外によって終了しました。

1: スレッドのライフサイクル

2: スレッド変換:

================================================= =======================

以下はデッドロック問題のデモンストレーションです

コードは以下のように表示されます。

public class ThreadTest {
    public static void main(String[] args) {

        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        new Thread(){
            @Override
            public void run() {

                synchronized (s1){
                    s1.append("a");
                    s2.append("1");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");

                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){
                    s1.append("c");
                    s2.append("3");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s1){
                        s1.append("d");
                        s2.append("4");

                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }){}.start();
    }
}

実行結果を図に示します。

プログラムが例外を報告せず、終了もしていないことがわかります。

次の例を見て理解してください。 

class A {
	public synchronized void foo(B b) {
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 进入了A实例的foo方法"); // ①
		try {
			Thread.sleep(200);
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 企图调用B实例的last方法"); // ③
		b.last();
	}

	public synchronized void last() {
		System.out.println("进入了A类的last方法内部");
	}
}

class B {
	public synchronized void bar(A a) {
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 进入了B实例的bar方法"); // ②
		try {
			Thread.sleep(200);
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
		System.out.println("当前线程名: " + Thread.currentThread().getName()
				+ " 企图调用A实例的last方法"); // ④
		a.last();
	}

	public synchronized void last() {
		System.out.println("进入了B类的last方法内部");
	}
}

public class DeadLock implements Runnable {
	A a = new A();
	B b = new B();

	public void init() {
		Thread.currentThread().setName("主线程");
		// 调用a对象的foo方法
		a.foo(b);
		System.out.println("进入了主线程之后");
	}

	public void run() {
		Thread.currentThread().setName("副线程");
		// 调用b对象的bar方法
		b.bar(a);
		System.out.println("进入了副线程之后");
	}

	public static void main(String[] args) {
		DeadLock dl = new DeadLock();
		new Thread(dl).start();
		dl.init();
	}
}

見てくれてありがとう!

おすすめ

転載: blog.csdn.net/qq_64976935/article/details/128893269