ホームページ  >  記事  >  Java  >  Java スレッドのステータスがブロックされました

Java スレッドのステータスがブロックされました

高洛峰
高洛峰オリジナル
2016-11-22 16:20:272207ブラウズ

BLOCKED 状態の定義

前に述べたように、BLOCKED の簡単な定義は次のとおりです:

モニター ロックを待ってブロックされているスレッドは、この状態にあります。 (モニターのロックを待ってブロックされたスレッドはこの状態になります。)

より詳細な定義については、Thread.State の javadoc を参照してください:

/**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

この文は非常に長いので、2 つの単純な文に分けることができます。理解できる文章。

ブロック状態のスレッドは、同期されたブロック/メソッドに入るモニターロックを待っています。

ブロック状態のスレッドは、同期されたブロックまたはメソッドに入るモニター ロックを待っています。

ブロック状態のスレッドは、Object.wait を呼び出した後、同期されたブロック/メソッドに再度入るためにモニター ロックを待機しています。

ブロック状態のスレッドは、同期されたブロックまたはメソッドに再度入るために Object.wait メソッドを呼び出した後、モニターのロックを待機しています。

同期ブロックに入る(入力する)ときにブロックする

まず最初の文について話します、これは理解しやすいです。

モニターロックは、複数のスレッド間の相互排他を実現するための同期アクセスに使用されます。したがって、スレッドがロックを取得して同期ブロックに入ると、それが外に出る前に、他のスレッドが入ろうとすると、ロックを取得できないため、同期ブロックの外でブロックされます。この時点では、状態は BLOCKED になります。

注: この状態の開始と終了は私たちの制御下にはありません。ロックが使用可能になると、スレッドはブロック状態から回復します。

このプロセスを示すためにいくつかのコードを使用できます:

@Test
public void testBlocked() throws Exception {
    class Counter {
        int counter;
        public synchronized void increase() {
            counter++;
            try {
                Thread.sleep(30000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    Counter c = new Counter();
    
    Thread t1 = new Thread(new Runnable() {
        public void run() {
            c.increase();
        }
    }, "t1线程");
    t1.start();
    
    Thread t2 = new Thread(new Runnable() {
        public void run() {
            c.increase();
        }
    }, "t2线程");
    t2.start();
    
    Thread.sleep(100); // 确保 t2 run已经得到执行
    assertThat(t2.getState()).isEqualTo(Thread.State.BLOCKED);
}

上記は、同期増加メソッドを使用してアクセス カウンター カウンターを定義しています。 t1 スレッドが最初に入力し、その後同期ブロックでスリープしたため、ロックが遅延しました。t2 が同期メソッドを実行しようとしたときに、ロックを取得できなかったためブロックされました。

VisualVM モニタリングは、t2 スレッドのステータスを示します。

Java スレッドのステータスがブロックされました

画像の「モニター」ステータスは、BLOCKED ステータスです。 t1 のスリープ中、t2 は BLOCKED 状態にあることがわかります。

BLOCKED 状態は、特別な種類の WAITING、特にロックを待機しているとみなすことができます。

同期ブロックに再入するときにブロックを待機します

次に 2 番目の文をもう一度見てください:

2. ブロックされた状態のスレッドは、Object wait を呼び出した後、同期ブロック/メソッドに再入するためにモニター ロックを待機しています。

ブロック状態のスレッドは、同期されたブロックまたはメソッドに再度入るために Object.wait メソッドを呼び出した後、モニターのロックを待機しています。

この文は少し複雑で、簡潔な中国語の文に翻訳するのは簡単ではありません。 wait に関連する背景をよく理解していないと、この文を理解するのは簡単ではありません。ここで少し詳しく説明してみましょう。再入力なので、2 回入力することになります。

wait メソッドの呼び出しは同期ブロック内にある必要があります。つまり、最初にロックを取得して同期ブロックに入る必要があります。入力。

waitを呼び出した後、ロックは解放され、このロックの待機キュー(wait set)に入れられます。

他のスレッドからnotifyまたはnotifyAll通知を受信した後、待機中のスレッドはすぐに実行を再開できません。これは、停止が同期ブロック内にあり、ロックが解放されているためです。そのため、再入力(reenter)する前にロックを再取得する必要があります。ブロックを同期し、その後、最後の待機ポイントから実行を再開します。 2回目の入場なので再入場といいます。

ただし、最初にロックが与えられるわけではありません。このプロセスは、実際には Enter プロセスと同じなので、ロックが占有されているために BLOCKED が発生する可能性もあります。他のスレッド。

このプロセスは、Object.wait を呼び出した後、同期されたブロック/メソッドを再入力すると呼ばれます。

このプロセスを示すためにコードの一部も使用します:

@Test
public void testReenterBlocked() throws Exception {
    class Account {
        int amount = 100; // 账户初始100元
        public synchronized void deposit(int cash) { // 存钱
            amount += cash;
            notify();
            try {
                Thread.sleep(30000); // 通知后却暂时不退出
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        public synchronized void withdraw(int cash) { // 取钱
            while (cash > amount) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            amount -= cash;
        }
    }
    Account account = new Account();
    
    Thread withdrawThread = new Thread(new Runnable() {
        public void run() {
            account.withdraw(200);
        }
    }, "取钱线程");
    withdrawThread.start();
    
    Thread.sleep(100); // 确保取钱线程已经得到执行
    
    assertThat(withdrawThread.getState()).isEqualTo(Thread.State.WAITING);
    
    Thread depositThread = new Thread(new Runnable() {
        public void run() {
            account.deposit(100);
        }
    }, "存钱线程");
    Thread.sleep(10000); // 让取钱线程等待一段时间
    depositThread.start();

    Thread.sleep(300); // 确保取钱线程已经被存钱线程所通知到

    assertThat(withdrawThread.getState()).isEqualTo(Thread.State.BLOCKED);
}

上記のコード シナリオの簡単な紹介:

入金メソッドと出金メソッドを持つアカウント オブジェクトがあり、初期金額は 100 元です。

お金の引き出しスレッドが最初に開始され、同期ブロックに入ります。お金が足りないことが判明すると、waitが呼び出され、ロックが解除され、スレッドがハングします。州)。

入金スレッドは 10 秒後に開始され、お金を入金して出金スレッドに通知しますが、その後同期ブロック内でスリープし続けるため、ロックが解放されません。

通知を受け取った後、出金スレッドは待機状態を抜けますが、ロックを保持していません。実行を再開するために同期ブロックに再入しようとすると、デポジットスレッドによってロックが解放されていないため、ブロックされます(BLOCKED)。州) )。

監視対象ディスプレイ:

Java スレッドのステータスがブロックされました

図に示すように、出金スレッドは最初はWAITINGであり、通知を受け取った後にロックを取得できないためブロック(BLOCKED)されます。

まとめ

この 2 つの文を一緒に見ると 2 つの意味がありますが、簡単に言うと Enter、reenter、または Enter という意味になります。ブロックを取得できない場合、スレッドは BLOCKED 状態になります。

スレッドが長時間 BLOCKED 状態にある場合は、デッドロックが発生していないかどうかを検討してください。

BLOCKED 状態は、従来の待機状態をさらに細分化した特別な種類の待機とみなすことができます。

Java スレッドのステータスがブロックされました WAITING 状態についてはまだ言及されておらず、ここでは wait メソッドが関係しているため、待機は次のようになります。これも上で少し説明しましたが、いくつかの分析を行った後、次の章で WAITING と TIMED_WAITING の 2 つの状態をより詳細に分析します。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。