>  기사  >  Java  >  Java 스레드 상태가 차단됨

Java 스레드 상태가 차단됨

高洛峰
高洛峰원래의
2016-11-22 16:20:272237검색

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,

이 문장은 매우 긴 , 이해하기 위해 두 개의 간단한 문장으로 나눌 수 있습니다.

차단된 상태의 스레드는 모니터 잠금이 동기화된 블록/메서드에 들어가기를 기다리고 있습니다.

차단된 상태의 스레드는 모니터 잠금이 동기화된 블록 또는 메서드에 들어가기를 기다리고 있습니다.

차단된 상태의 스레드는 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 스레드가 먼저 진입한 후 동기화 블록에서 sleep하여 잠금이 지연되었습니다. t2가 동기화 방법을 실행하려고 했으나 잠금을 획득하지 못해 차단되었습니다.

VisualVM 모니터링은 t2 스레드의 상태를 보여줍니다.

Java 스레드 상태가 차단됨

사진의 "모니터" 상태는 BLOCKED 상태입니다. t1 sleep 중에 t2가 BLOCKED 상태인 것을 볼 수 있습니다.

BLOCKED 상태는 특별한 종류의 WAITING, 특히 잠금을 기다리는 것으로 간주될 수 있습니다.

대기 후 동기화 블록 재진입 시 차단

이제 두 번째 문장을 다시 살펴보세요.

차단된 상태의 스레드가 모니터 잠금이 재진입되기를 기다리고 있습니다. Object.wait를 호출한 후 동기화된 블록/메서드.

차단된 상태의 스레드는 동기화된 블록이나 메서드에 다시 들어가기 위해 Object.wait 메서드를 호출한 후 모니터 잠금을 기다리고 있습니다.

이 문장은 좀 복잡해서 간결한 중국어 문장으로 번역하기가 쉽지 않습니다. 기다림과 관련된 배경을 제대로 이해하지 않고서는 이 문장을 이해하기가 쉽지 않습니다. 여기서 조금 더 확장해 보겠습니다. 재진입이므로 두 번의 진입이 있다는 뜻입니다.

호출 대기 메소드는 동기화된 블록에 있어야 합니다. 즉, 먼저 잠금을 획득하고 동기화된 블록에 진입해야 합니다. 첫 번째 입력입니다.

wait를 호출한 후 잠금이 해제되고 이 잠금의 대기 대기열(대기 세트)에 들어갑니다.

다른 스레드로부터 통지 또는 통지를 받은 후 대기 스레드는 중지가 동기화 블록에 있고 잠금이 해제되었기 때문에 즉시 실행을 재개할 수 없으므로 다시 시작하기 전에 잠금을 다시 획득해야 합니다. 동기화된 블록을 다시 입력하고 마지막 대기부터 실행을 재개합니다. 이번이 2번째 입장이므로 재입장이라고 합니다.

그러나 잠금이 먼저 부여되지는 않습니다. 스레드는 여전히 잠금을 위해 다른 스레드와 경쟁해야 합니다. 이 프로세스는 실제로 Enter 프로세스와 동일하므로 잠금이 설정되어 있기 때문일 수도 있습니다. 다른 스레드가 이미 점유하고 있습니다.

이 프로세스를 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위안입니다.

먼저 자금 출금 스레드가 시작되어 (입력) 동기화 블록에 진입합니다. 200위안을 출금하려고 시도하지만 자금이 부족하다는 사실이 발견되어 대기가 호출되고 잠금이 해제됩니다. 정지(WAITING 상태).

10초 후에 입금 스레드가 시작되어 돈을 입금하고 출금 스레드에 알렸지만 이후 동기화 블록에서 계속 sleep 상태가 되어 잠금이 해제되지 않습니다.

알림을 받은 후 출금 스레드는 WAITING 상태를 종료하지만 더 이상 잠금을 유지하지 않습니다. 실행을 재개하기 위해 동기화 블록에 다시 진입하려고 하면 입금 스레드에서 잠금이 해제되지 않았기 때문에 차단되었습니다(BLOCKED 상태).

모니터링되는 디스플레이:

Java 스레드 상태가 차단됨

사진과 같이 출금 쓰레드가 먼저 WAITING 상태이고, 잠금을 획득할 수 없어 알림을 받은 후 차단(BLOCKED)됩니다.

요약

이 두 문장을 함께 보면 두 가지 의미가 있지만, 사실은 여전히 ​​같은 의미입니다. 간단히 말하면 enter, reenter 또는 enter입니다.

잠금을 얻을 수 없어 동기화된 블록에 들어갈 수 없는 경우 스레드는 BLOCKED 상태입니다.

스레드가 오랫동안 BLOCKED 상태에 있다면 교착상태가 발생했는지 살펴보세요.

BLOCKED 상태는 전통적인 대기 상태의 하위 분류인 특별한 종류의 대기로 간주할 수 있습니다.

Java 스레드 상태가 차단됨

WAITING 상태가 아니기 때문에 여기에는 wait 메서드가 포함되어 있으므로 위에서 대기에 대한 약간의 분석을 수행했습니다. 다음 장에서는 WAITING 및 TIMED_WAITING 상태를 더 자세히 분석하겠습니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.