CountdownLatch 소스 코드 분석 - Countdown ()
이전 기사는 소스 코드 레벨에서 CountdownLatch의 Await ()의 원리에 대해 이야기했습니다. 이 기사에서는 Countdown ()에 대해 이야기합니다.
public void countdown () {// countdownlatch sync.releaseShared (1);} ↓ Public Final Boolean ReleasShared (int arg) {// aqs if (tryReleasShared (arg)) {doreLeasShared (); 진실을 반환하십시오. } return false;} ↓ 보호 된 Boolean tryreleaseShared (int 릴리스) {//countdownlatch.sync // 감소 카운트; (;;) {int c = getState ()에 대해 0으로 전환 할 때 신호. if (c == 0) false를 반환합니다. int nextc = c-1; if (compareAndsetState (c, nextc)) return nextc == 0; }}생성자 CountdownLatch End = New CountdownLatch (2)를 통해; 상태는 2로 설정되므로 C == 2, NextC = 2-1,
그런 다음 다음 CAS 작업을 통해 상태를 1로 설정하십시오.
보호 된 최종 부울 CompareAndsetState (int expect, int update) {//이 반환을 지원하려면 Intrinsics 설정에 대해서는 아래를 참조하십시오. }현재 Nextc는 0이 아니며 False를 반환합니다. countdown () 메소드가 두 번, state == 0, nextc == 0이라고 부를 때까지 기다렸다가이 시점에서 true를 반환합니다.
DoreLeasShared () 메소드를 입력하십시오.
DoreLeasShared (); ↓ 개인 void doreLeasShared () { / * * 다른 * 진행 중에서 획득 /릴리스가 있더라도 릴리스가 전파되는지 확인하십시오. 이것은 신호가 필요한 경우 헤드를 자극하지 않도록하는 일반적인 * * * * 그러나 그렇지 않은 경우, 상태는 릴리스되면 전파가 계속되도록 전파되도록 설정됩니다. * 또한 새 노드가 추가되는 경우 * 가이 작업을 수행하는 동안 루프해야합니다. 또한 * unparksuccessor *의 다른 사용과 달리 CAS가 재설정 상태 *가 실패하는지 알아야합니다. */ for (;;) {노드 h = 헤드; if (h! = null && h! = 꼬리) {int ws = h.waitstatus; if (ws == node.signal) {if (! compareandsetwaitstatus (h, node.signal, 0)) 계속; // 사례를 다시 확인하기 위해 루프를 다시 확인하지 않습니다 (h); } else if (ws == 0 &&! CompareAndSetwaitStatus (h, 0, node.propagate)) 계속; // 실패한 cas}에서 루프} if (h == head) // 헤드가 변경된 경우 루프; }}현재 대기 대기열 모델을 리콜합니다.
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
현재 머리는 늘어나지 않거나 꼬리가 아닙니다. waitstatus == node.signal이므로 if (! compareandsetwaitstatus (h, node.signal, 0))를 입력하십시오.
if (! compareAndsetwaitstatus (h, node.signal, 0)) ↓ /*** 노드의 Cas waitstatus 필드. */private static final boolean boolean boolean compareandsetwaitstatus (노드 노드, int eppler, int update) {return unsafe.compareandswapint (node, waitstatusoffset, expling, update);}이 CAS 작동은 상태를 0으로 설정합니다. 이는 현재 헤드의 웨이 스타 우스가 0임을 의미합니다. 큐 모델은 다음과 같습니다
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
이 메소드는 true를 반환합니다. UnparkSuccessor (H)를 입력하십시오.
unparksuccessor (H); ↓ 개인 void unparksuccessor (노드 노드) { / * * 상태가 음수 인 경우 (즉, 필요한 신호) 신호를 기대할 때 제거하려고 시도합니다. 이 *가 실패하거나 대기 스레드에 의해 상태가 변경되면 괜찮습니다. */ int ws = node.waitstatus; if (ws <0) compareandsetwaitstatus (노드, ws, 0); / * * unpark에서 스레드는 후속에 보관됩니다. 일반적으로 다음 노드입니다. 그러나 취소되거나 명백하게 무효가되면 * 실제 * 비 캔셀되지 않은 후계자를 찾기 위해 꼬리에서 뒤로 이동합니다. */ node s = node.next; if (s == null || s.waitstatus> 0) {s = null; for (node t = tail; t! = null && t! = 노드; t = t.prev) if (t.waitstatus <= 0) s = t; } if (s! = null) Locksupport.unpark (s.thread);}S는 헤드의 후속 노드, 즉 현재 스레드가있는 노드입니다. s! = null 및 s.waitstatus == 0이므로 Locksupport.unpark (s.thread)를 입력하십시오.
public static void unpark (스레드 스레드) {if (스레드! = null) 불안하지 않은. }즉, 잠금 해제 된 스레드가 차단되었습니다. 심판은 호루라기를 날릴 수있었습니다!
Countdown ()의 원리는 매우 명확합니다.
Countdown () 메소드가 실행될 때마다 상태가 1만큼 줄어 듭니다. 상태 == 0까지 큐에서 차단 된 스레드가 해제되기 시작하고 후속 노드의 스레드가 전임 노드의 웨이터 상태에 따라 해제됩니다.
좋아, 이전 기사의 질문으로 돌아가서 다음 루프가 언제 나올 것인가 (Await 방법의 루프).
for (;;) {최종 노드 p = node.predecessor (); if (p == head) {int r = tryacquireshared (arg); if (r> = 0) {SetheadandPropagate (Node, R); p.next = null; // gc 실패 = false; 반품; }} if (whildparkfterfailedacquire (p, node) && parkandcheckinterrupt ()) 던질 새로운 InterruptedException ();}현재 상태 == 0이므로 SetheadandPropagate 방법을 입력하십시오.
세 테드 랜드 프로파이트 (Node, R); ↓ 개인 void setheadandpropagate (노드 노드, int provagate) {node h = head; // sethead (노드) 아래 점검을 위해 오래된 헤드를 기록합니다. / * * 다음 대기열 노드를 알리십시오. * 전파는 발신자에 의해 표시되었거나 * 또는 이전 작업에 의해 H.WaitStatus *로 기록되었습니다 * (참고 : 이것은 웨이크 스타 더스의 부호 검사를 사용하여 웨이터의 서명 검사를 사용하여 신호로 전파 될 수 있기 때문에 *와 * 다음 노드가 공유 모드에서 대기 할 수 있기 때문에 * * * * * * * * * * * * * * * * * * * * * * * * * *는 널리 보존 된 것입니다. * 불필요한 모닝 업이지만 여러 번의 레이싱 획득/릴리스가있을 때만 현재 또는 곧 신호가 필요합니다. */ if (propagate> 0 || h == null || h.waitstatus <0 || (h = head) == null || h.waitstatus <0) {node s = node.next; if (s == null || s.isshared ()) doreleaseshared (); }} ↓ 개인 void sethead (노드 노드) {head = 노드; node.thread = null; node.prev = null;}이 방법은 머리의 후속 노드를 머리로 변경합니다. 이 메소드 후에 노드의 다음 노드가 NULL로 설정되고 모델은 다음 그림이됩니다.
이전 +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
즉, 노드 헤드 테일과 다른 것들이 널로 설정되어 GC가 재활용되기를 기다립니다. 현재로서 반환하고 For Loop에서 뛰어 내리면 대기열이 지워집니다.
다음은 전체 프로세스의 시연입니다
세 테드 랜드 프로파이트 (Node, R); +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 스레드 = null | <---- 노드 (꼬리) | Currentthread | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 노드 (꼬리) | Currentthread | + -------------------------------------+ ↓ +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CountdownLatch의 핵심은 차단 스레드 큐로, waitstatus가 후속 노드 스레드 상태를 설명하는 스레드 및 웨이 스타 우스를 포함하는 링크 된 목록에서 구성된 큐입니다.
상태는 매우 중요한 깃발입니다. 구성 할 때 해당 N 값으로 설정됩니다. n! = 0이면 스레드가 중단되지 않는 한 블록 큐는 항상 차단됩니다.
Countdown () 메소드가 호출 될 때마다 State-1이 사용되며 Await () 메소드는 State == 0까지 메소드를 차단 대기열에 호출하는 스레드를 추가하는 데 사용되며 스레드를 해제 할 수 없습니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.