-
동일한 프로세스의 여러 스레드가 동일한 저장 공간을 공유하기 때문에 편의성을 제공하면서도 심각한 접근 충돌 문제를 야기합니다. Java 언어는 이 충돌을 해결하기 위한 특별한 메커니즘을 제공하여 동일한 데이터 개체가 동시에 여러 스레드에서 액세스되는 것을 효과적으로 방지합니다.
프라이빗 키워드를 사용하면 메서드를 통해서만 데이터 객체에 액세스할 수 있으므로 메서드에 대한 메커니즘만 제안하면 됩니다. 이 메커니즘은 동기화된 메서드와 동기화된 블록이라는 두 가지 용도를 포함하는 동기화된 키워드입니다.
1. 동기화 메소드: 메소드 선언에 동기화 키워드를 추가하여 동기화 메소드를 선언합니다. 좋다:
공개 동기화 무효 accessVal(int newVal);
동기화된 메서드는 클래스 멤버 변수에 대한 액세스를 제어합니다. 각 클래스 인스턴스는 잠금에 해당합니다. 각 동기화된 메서드는 실행되기 전에 메서드를 호출하는 클래스 인스턴스의 잠금을 획득해야 합니다. 그렇지 않으면 해당 클래스가 속한 스레드가 차단됩니다. 메서드가 실행되면 클래스 멤버 변수를 독점적으로 차지합니다. 이 메서드에서 반환될 때까지 잠금은 해제되지 않습니다. 차단된 스레드는 잠금을 획득하고 실행 가능 상태로 다시 들어갈 수 있습니다. 이 메커니즘은 각 클래스 인스턴스에 대해 동시에 동기화됨으로 선언된 모든 멤버 함수 중 최대 하나가 실행 가능 상태가 되도록 보장합니다(최대 하나는 클래스 인스턴스에 해당하는 잠금을 얻을 수 있기 때문입니다)(이 문에 주의하세요). ! 클래스 인스턴스에 대한 잠금이므로 한 번에 하나의 개체에 대해 한 번에 하나의 동기화된 메서드만 실행되므로 클래스 멤버 변수의 액세스 충돌을 효과적으로 방지할 수 있습니다. 액세스 클래스 멤버 변수는 동기화된 것으로 선언됩니다.
Java에서는 클래스 인스턴스뿐만 아니라 각 클래스도 잠금에 해당합니다. 이러한 방식으로 클래스의 정적 멤버 변수에 대한 액세스를 제어하기 위해 클래스의 정적 멤버 함수를 선언할 수도 있습니다.
동기화된 메소드의 결함: 큰 메소드가 동기화됨으로 선언되면 효율성이 크게 영향을 받습니다. 일반적으로 스레드 클래스 메소드 run()이 동기화됨으로 선언되면 스레드의 전체 수명 주기 동안 계속 실행됩니다. . 이 클래스의 동기화된 메서드에 대한 호출이 절대 성공하지 못하게 됩니다. 물론 클래스 멤버 변수에 접근하는 코드를 특수 메소드에 넣어서 동기화된 것으로 선언하고 이를 메인 메소드에서 호출하면 이 문제를 해결할 수 있지만, 자바는 더 나은 솔루션을 제공하는데, 바로 동기화 블록이다.
2. 동기화 블록: 동기화 키워드를 통해 동기화 블록을 선언합니다. 구문은 다음과 같습니다.
동기화(syncObject) {
//액세스 제어를 허용하는 코드
}
동기화된 블록은 코드가 실행되기 전에 개체 syncObject(앞서 언급한 것처럼 클래스 인스턴스 또는 클래스일 수 있음)의 잠금을 획득해야 하는 코드 블록입니다. 어떤 코드블록이라도 대상으로 할 수 있고 잠긴 객체를 임의로 지정할 수 있어 유연성이 높다.
스레드 차단(동기화)
공유 저장 영역에 대한 액세스 충돌을 해결하기 위해 Java는 동기화 메커니즘을 도입했습니다. 이제 여러 스레드에 의한 공유 리소스 액세스를 살펴보겠습니다. 언제든지 필요한 리소스가 충분하지 않을 수 있으므로 동기화 메커니즘으로는 더 이상 충분하지 않습니다. 준비. 액세스하기 위해 동시에 둘 이상의 리소스가 준비될 수 있습니다. 이 경우 액세스 제어 문제를 해결하기 위해 Java는 차단 메커니즘 지원을 도입했습니다.
차단이란 특정 조건(예: 리소스 준비 중)이 발생할 때까지 스레드 실행을 일시 중지하는 것을 의미합니다. Java는 차단을 지원하는 수많은 메소드를 하나씩 분석해 보겠습니다.
1. sleep() 메소드: sleep()을 사용하면 밀리초 단위의 시간을 매개변수로 지정할 수 있습니다. 이 방법을 사용하면 지정된 시간 내에 스레드가 차단 상태로 들어가고 지정된 시간이 지나면 CPU 시간을 얻을 수 없습니다. 스레드는 실행 가능 상태로 다시 들어갑니다.
일반적으로 sleep()은 리소스가 준비될 때까지 기다릴 때 사용됩니다. 테스트에서 조건이 충족되지 않은 것을 확인한 후 일정 시간 동안 스레드를 차단한 다음 조건이 충족될 때까지 다시 테스트합니다.
2. suspens() 및 이력서() 메소드(교착 상태를 일으키기 쉬움, 구식): 두 메소드를 함께 사용하면 스레드가 차단 상태에 들어가고 해당 이력서()가 자동으로 복구되지 않습니다. 호출되면 스레드가 실행 가능 상태로 다시 들어갈 수 있습니다. 일반적으로 다른 스레드에서 생성된 결과를 기다릴 때 suspens() 및 이력서()가 사용됩니다. 테스트에서 결과가 생성되지 않은 것으로 확인되면 스레드가 차단되고 다른 스레드가 결과를 생성한 후에 이력서()를 호출합니다. 그것을 재개합니다.
3. Yield() 메서드: Yield()는 스레드가 현재 할당된 CPU 시간을 포기하도록 하지만 스레드를 차단하지는 않습니다. 즉, 스레드는 여전히 실행 가능한 상태에 있으며 다음 시점에 다시 CPU 시간을 할당받을 수 있습니다. 언제든지. Yield() 호출의 효과는 스레드가 다른 스레드로 이동할 만큼 충분한 시간을 실행했다고 간주하는 스케줄러와 동일합니다.
4. wait() 및 inform() 메소드: 두 메소드는 스레드가 차단 상태로 들어가도록 합니다. 하나는 매개변수로 기간을 지정하는 것을 허용합니다. 다른 매개변수는 그렇지 않습니다. 전자는 해당 inform()이 호출되거나 지정된 시간을 초과하면 실행 가능 상태로 다시 들어가고, 후자는 해당 inform()이 호출될 때 호출되어야 합니다.
언뜻 보기에는 suspens() 및resume() 메서드 쌍과 다르지 않은 것처럼 보이지만 실제로는 완전히 다릅니다. 핵심 차이점은 위에 설명된 모든 방법이 차단 시 점유된 잠금(점유된 경우)을 해제하지 않는 반면, 이 반대 규칙은 그 반대라는 것입니다.
위의 핵심 차이점은 일련의 세부적인 차이점으로 이어집니다.
우선 위에서 설명한 모든 메서드는 Thread 클래스에 속하지만 이 쌍은 바로 Object 클래스에 속합니다. 즉, 모든 개체에는 이 메서드 쌍이 있습니다. 처음에는 믿을 수 없을 것 같지만 실제로는 매우 자연스러운 일입니다. 왜냐하면 이 메서드 쌍이 차단되면 점유된 잠금이 해제되어야 하고 개체의 wait() 메서드를 호출하면 해당 잠금이 소유되기 때문입니다. 스레드를 차단하고 개체에 대한 잠금이 해제됩니다. 개체의 inform() 메서드를 호출하면 해당 개체의 wait() 메서드를 호출하여 차단된 무작위로 선택된 스레드가 차단 해제됩니다(그러나 잠금을 얻을 때까지 실행되지는 않습니다).
둘째, 위에 설명된 모든 메소드는 어느 위치에서나 호출할 수 있지만 이 메소드 쌍(wait() 및 inform())은 동기화된 메소드 또는 블록에서 호출되어야 하며 그 이유도 매우 간단합니다. or block 현재 스레드만이 잠금을 차지하고 잠금이 해제될 수 있습니다. 마찬가지로, 이 메서드 쌍을 호출하는 개체에 대한 잠금은 현재 스레드가 소유해야 잠금이 해제될 수 있습니다. 따라서 메서드 호출 쌍은 메서드 쌍을 호출하는 개체인 잠긴 개체가 있는 동기화된 메서드나 블록에 배치되어야 합니다. 이 조건이 충족되지 않으면 프로그램이 여전히 컴파일될 수 있지만 런타임 시 IllegalMonitorStateException 예외가 발생합니다.
wait() 및 inform() 메소드의 위 특성은 동기화된 메소드 또는 블록과 함께 사용되는 경우가 많다는 것을 결정합니다. 이를 운영 체제의 프로세스 간 통신 메커니즘과 비교하면 유사점이 드러납니다. 동기화된 메소드 또는 블록은 유사한 기능을 제공합니다. 운영 체제 프리미티브의 기능에 대해 해당 실행은 멀티스레딩 메커니즘에 의해 방해받지 않으며 이 대응은 블록 및 웨이크업 프리미티브와 동일합니다(이 메소드 쌍은 모두 동기화된 것으로 선언됩니다). 이들의 조합을 통해 운영 체제에서 일련의 정교한 프로세스 간 통신 알고리즘(예: 세마포어 알고리즘)을 구현할 수 있으며 다양하고 복잡한 스레드 간 통신 문제를 해결하는 데 사용할 수 있습니다.
wait() 및 inform() 메서드에 대한 두 가지 마지막 사항은 다음과 같습니다.
첫째, inform() 메소드를 호출하여 차단 해제된 스레드는 객체의 wait() 메소드를 호출하여 차단된 스레드 중에서 무작위로 선택되므로 프로그래밍할 때 특히 주의해야 합니다. 이러한 불확실성으로 인해 발생하는 문제.
둘째: inform() 외에도 유사한 역할을 수행할 수 있는 informAll() 메소드도 있습니다. 유일한 차이점은 informAll() 메소드를 호출하면 wait() 메소드를 호출하여 차단된 모든 스레드가 제거된다는 것입니다. 한 번에 모든 개체가 차단 해제되었습니다. 물론, 잠금을 획득한 스레드만이 실행 가능 상태로 들어갈 수 있습니다.
차단에 대해 이야기할 때 교착 상태에 대해 이야기해야 합니다. 간략한 분석을 통해 시간 초과 기간을 지정하지 않은 suspens() 메서드와 wait() 메서드 호출 모두 교착 상태를 일으킬 수 있음을 알 수 있습니다. 안타깝게도 Java는 언어 수준에서 교착 상태 방지를 지원하지 않으므로 프로그래밍 시 교착 상태를 피하도록 주의해야 합니다.
위에서 우리는 Java에서 스레드 차단의 다양한 방법을 분석했습니다. 가장 강력하고 사용하기 가장 유연한 wait() 및 inform() 메소드에 중점을 두었지만 이로 인해 효율성이 더 낮습니다. 오류가 발생하기 쉽습니다. 실제 사용에서는 목표를 더 잘 달성하기 위해 다양한 방법을 유연하게 사용해야 합니다.
데몬 스레드
데몬 스레드는 특수한 유형의 스레드입니다. 일반 스레드와 차이점은 애플리케이션의 데몬이 아닌 스레드가 모두 종료되면 여전히 데몬 스레드가 실행 중이더라도 애플리케이션이 종료된다는 것입니다. 반면에 데몬이 아닌 스레드가 실행되는 동안 애플리케이션은 종료되지 않습니다. 데몬 스레드는 일반적으로 백그라운드에서 다른 스레드에 서비스를 제공하는 데 사용됩니다.
isDaemon() 메서드를 호출하여 스레드가 데몬 스레드인지 여부를 확인하거나 setDaemon() 메서드를 호출하여 스레드를 데몬 스레드로 설정할 수 있습니다.
스레드 그룹
스레드 그룹은 Java 고유의 개념입니다. Java에서 스레드 그룹은 ThreadGroup 클래스의 객체입니다. 각 스레드는 고유한 스레드 그룹에 속하며 이 스레드 그룹은 스레드가 생성될 때 지정되며 전체 수명 주기 동안 사용할 수 없습니다. 스레드를 변경합니다. ThreadGroup 유형 매개변수가 포함된 Thread 클래스 생성자를 호출하여 스레드가 속한 스레드 그룹을 지정할 수 있습니다. 지정하지 않으면 스레드는 system이라는 시스템 스레드 그룹으로 기본 설정됩니다.
Java에서는 사전 구축된 시스템 스레드 그룹을 제외한 모든 스레드 그룹을 명시적으로 생성해야 합니다. Java에서는 시스템 스레드 그룹을 제외한 각 스레드 그룹이 다른 스레드 그룹에 속합니다. 지정하지 않으면 기본적으로 시스템 스레드 그룹에 속합니다. 이러한 방식으로 모든 스레드 그룹은 시스템 스레드 그룹을 루트로 하는 트리를 형성합니다.
Java를 사용하면 스레드 그룹의 모든 스레드에 대해 동시에 작업을 수행할 수 있습니다. 예를 들어 스레드 그룹의 해당 메서드를 호출하여 그 안에 있는 모든 스레드의 우선 순위를 설정할 수 있으며, 또한 스레드 그룹의 모든 스레드를 시작하거나 차단할 수도 있습니다. 그것.
Java 스레드 그룹 메커니즘의 또 다른 중요한 역할은 스레드 안전성입니다. 스레드 그룹 메커니즘을 사용하면 그룹화를 통해 서로 다른 보안 특성을 가진 스레드를 구별하고, 서로 다른 그룹의 스레드를 다르게 처리하며, 스레드 그룹의 계층 구조를 통해 동일하지 않은 보안 조치의 채택을 지원할 수 있습니다. Java의 ThreadGroup 클래스는 스레드 그룹 트리의 각 스레드 그룹과 스레드 그룹의 각 스레드를 작동하는 데 도움이 되는 많은 수의 메소드를 제공합니다.
스레드 상태 특정 시점에 스레드는 하나의 상태에만 있을 수 있습니다.
새로운
아직 시작되지 않은 스레드는 이 상태입니다.
실행 가능
JVM(Java Virtual Machine)에서 실행 중인 스레드는 이 상태에 있습니다.
막힌
차단되어 모니터 잠금을 기다리는 스레드가 이 상태입니다.
대기 중
다른 스레드가 특정 작업을 수행하기를 무한정 기다리는 스레드가 이 상태입니다.
대기 중인 스레드의 스레드 상태입니다. 스레드가 다음 메서드 중 하나를 호출했기 때문에 대기 상태에 있습니다.
시간 초과 값이 없는 Object.wait
시간 초과 값이 없는 Thread.join
LockSupport.park
대기 상태의 스레드는 다른 스레드가 특정 작업을 수행하기를 기다리고 있습니다. 예를 들어, 객체에 대해 Object.wait()를 호출한 스레드는 다른 스레드가 해당 객체에 대해 Object.notify() 또는 Object.notifyAll()을 호출하기를 기다리고 있습니다. Thread.join()을 호출한 스레드는 지정된 스레드가 종료되기를 기다리고 있습니다.
TIMED_WAITING
지정된 대기 시간에 따라 다른 스레드가 작업을 수행하기를 기다리는 스레드가 이 상태입니다.
지정된 대기 시간이 있는 대기 스레드의 스레드 상태입니다. 스레드는 지정된 긍정적인 대기 시간으로 다음 메서드 중 하나를 호출하여 시간 제한 대기 상태에 있습니다.
스레드.수면
시간 초과 값이 있는 Object.wait
시간 초과 값이 있는 Thread.join
LockSupport.parkNanos
LockSupport.parkUntil
종료됨
종료된 스레드가 이 상태입니다.