자세한 내용 은 조건의 대기 및 신호 대기 / 알림 메커니즘에 대한 자세한 설명을 참조하십시오.
전체적으로 Object의 대기 및 알림 / 알림은 스레드 간의 대기 / 알림 메커니즘을 완료하기 위해 개체 모니터와 협력하는 것입니다.
그리고 Condition과 Lock이 협력하여 대기 알림 메커니즘을 완료합니다.
전자는 Java의 최하위 수준에 있고 후자는 제어 가능성과 확장 성이 더 높은 언어 수준 입니다.
두 가지를 사용하는 다른 방법 외에도 기능적 특성 에는 여전히 많은 차이 가 있습니다 .
- 조건은 비 응답 인터럽트를 지원할 수 있지만 객체 모드를 사용해서는 안됩니다.
- Condition은 여러 대기 대기열 (새 여러 Condition 객체)을 지원할 수 있지만 Object 모드는 하나만 지원할 수 있습니다.
- 조건은 제한 시간 설정을 지원할 수 있지만 Object는 지원하지 않습니다.
1. 조건 구현의 원리
1. 대기 대기열의 실현 원리 :
조건 개체 빌드가 전달 되고이 lock.newCondition(),
메서드는 실제로 AQS의 내부 클래스 인 ConditionObject 개체를 만듭니다 .
잠금 메커니즘의 구현에서 AQS는 내부적으로 동기화 대기열을 유지합니다. 배타적 잠금 인 경우 잠금을 획득하지 못한 모든 스레드의 꼬리가 동기화 대기열에 삽입 됩니다 . 마찬가지로 조건은 내부적으로도 사용됩니다. 같은 방식으로, 하나는. 내부적으로 유지되는 대기 큐에 대한 의 condition.await 메소드를 호출하는 모든 스레드가 대기 큐에 추가되며, 스레드의 상태는로 변환되어 대기 상태입니다.
ConditionObject에는 firstWaiter 및 lastWaiter의 두 가지 멤버 변수가 있습니다 .
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
ConditionObject가 대기 큐의 헤드 및 테일 포인터를 보유하여 대기 큐를 관리하는 것을 볼 수 있습니다.
Node 클래스는 AQS에서 Node 클래스를 재사용하며 노드 상태 및 관련 속성은 AQS 구현에서 볼 수 있습니다.
Node 클래스에는 다음과 같은 속성이 있습니다.
// 후속 노드
노드 nextWaiter;
대기 대기열은 단방향 대기열 이며 이전에 AQS라고 말했을 때 동기화 대기열 이 양방향 대기열 이라는 것을 알았습니다 .
- condition.await 메서드를 호출 한 후 스레드는 그림과 같이 대기 큐에 차례로 삽입되며 큐의 스레드 참조는 Thread-0, Thread-1, Thread-2 ... Thread-8입니다.
- 대기 대기열은 단방향 대기열입니다.
lock.newCondition () 메서드를 여러 번 호출하여 여러 조건 객체 를 만들 수 있습니다 . 즉, 잠금 은 여러 대기 대기열을 보유 할 수 있습니다 .
Object를 사용하는 이전 방법은 실제로 Object Object 모니터에 하나의 동기화 대기열과 하나의 대기 대기열 만있을 수 있음을 의미합니다 .
동시 패키지의 잠금에는 동기화 대기열과 여러 대기 대기열이 있습니다.
2. 기다림의 원칙 :
현재 스레드가 condition.await () 메서드를 호출 한 후 현재 스레드는 잠금을 해제 한 다음 signal / signalAll이 될 때까지 대기 큐에 추가합니다. 그러면 현재 스레드가 대기 큐에서 동기화로 이동합니다. 잠금을 획득 할 때까지 큐 그런 다음 await 메서드에서 반환하거나 기다리는 동안 중단 될 때 중단됩니다 .
2.1 대기 대기열에 현재 스레드를 추가하는 방법은 무엇입니까?
현재 쓰레드에 의해 캡슐화 된 Node는 tail 삽입을 통해 wait queue에 삽입 할 수 있습니다 . 동시에 wait queue는 선행 노드가없는 chain queue 임을 알 수 있습니다 . AQS를 배우기 전에 우리는 동기화가 queue 는 선두 노드였습니다. Chain queue , 이것은 둘의 차이입니다.
2.2 잠금을 해제하는 과정?
AQS 템플릿 메서드 해제 메서드를 호출하여 AQS의 동기화 상태를 해제하고 동기화 대기열에서 헤드 노드의 후속 노드가 참조하는 스레드를 깨 웁니다 . 해제가 성공하면 정상적으로 반환되고 실패하면 예외가 발생합니다.
2.3 await 메소드를 종료하려면 어떻게해야합니까?
현재 스레드가 중단되거나 condition.signal / condition.signalAll 메서드가 호출됩니다. 현재 노드가 동기화 대기열로 이동 한 후 현재 스레드가 await 메서드를 종료하기위한 전제 조건입니다.
while 루프를 종료 한 후 acquireQueued(node, savedState)
그는 AQS 기본 구현의 도입에 대해이 메서드를 호출 할 것입니다. 관심이 있다면 이 기사 를 살펴볼 수 있습니다. 이 메서드의 역할은 동기화 상태를 얻기위한 지속적인 시도의 스레드 회전 프로세스에 있습니다. 성공할 때까지 (스레드가 잠금을 얻음) . 이것은 또한 await 메소드 의 종료가 조건 참조 (association)를 획득 한 잠금이어야 함을 보여줍니다 .
3. 신호 및 signalAll의 실현 원리 :
signal 또는 signalAll 조건 메서드를 호출하면 대기 큐에서 대기 시간이 가장 긴 노드를 동기화 큐로 이동할 수 있으므로 노드가 잠금을 얻을 수 있습니다. 대기 큐는 선입 선출 (FIFO)에 따라 대기 큐의 헤드 노드는 대기 시간이 가장 긴 노드, 즉 조건의 신호 메서드가 호출 될 때마다 헤드 노드 여야합니다. 동기 큐로 이동됩니다.
조건 신호를 호출하기위한 전제 조건은 현재 스레드가 잠금을 획득 한 것입니다.이 메서드는 대기 대기열에있는 헤드 노드, 즉 대기 시간이 가장 긴 노드를 동기화 대기열로 이동하고 대기중인 스레드를 깨울 기회가 있기 전에 동기화 큐가 깨어났습니다. 즉, await 메서드의 LockSupport.park (this) 메서드에서 반환되어 await 메서드를 호출 한 스레드가 성공적으로 종료되도록 할 수 있습니다. .
signalAll과 signal 메서드의 차이점은 doSignalAll 메서드에 반영되어 있습니다. doSignal 메서드 는 대기중인 큐의 헤드 노드에서만 작동 한다는 것을 이미 알고 있습니다. 이 메서드는 대기중인 큐의 모든 노드가 동기화 큐로 이동한다는 사실 만 인식합니다 . 즉, 현재 condition.await () 메서드를 호출하는 각 스레드에 "알림"입니다.
4. 두 가지 생각을 결합 :
대기 알림 메커니즘 은 조건 에서 제공하는 await 및 signal / signalAll 메소드를 사용하여 구현할 수 있으며 ,이 메커니즘은 가장 고전적인 문제인 "생산자 및 소비자 문제"를 해결할 수 있습니다 .
이 사진은 매우 중요합니다. 이 그림을 이해하고 기억한다고 말할 수 있습니다!