CountdownLatch 소스 코드 분석 - Await (), 특정 컨텐츠는 다음과 같습니다.
이전 기사는 CountdownLatch 사용 방법에 대해 이야기했습니다. 이 기사는 소스 코드 수준에서 Await ()의 원리에 대해 이야기합니다.
우리는 래치 카운트가 0이 될 때까지 (또는 스레드 인터럽트) 기다릴 수 있다는 것을 이미 알고 있습니다.
아래는 소스 코드입니다.
end.await (); ↓ public void await ()가 중단 exception {sync.acquiresharedinterpruptility (1);}동기화는 CountdownLatch의 내부 클래스입니다. 다음은 정의입니다.
개인 정적 최종 클래스 동기화 확장 AbstractQueuedSynchronizer {...}AbstractQueuedSynchronizer를 상속합니다. AbstractQueuedSynchronizer이 클래스는 Java 스레드에서 매우 중요한 클래스에 속합니다.
FIFO 대기 대기열에 의존하는 차단 잠금 장치 및 관련 동기화제 (예 : 신호, 이벤트 등)를 구현하는 프레임 워크를 제공합니다.
계속 가서 AbstractQueuedSynchronizer 클래스로 이동하십시오.
동기화. ↓ Public Final Avoid AcquiraredInterpruptibly (int arg) // AbstractQueuedSynchronizer는 InterpruptedException {if (thread.interrupted ()) 던지기 새로운 InterruptedException (); if (tryacquireshared (arg) <0) doacquiresharedinterpruptility (arg);}여기에는 두 가지 판단이 있습니다. 먼저 스레드가 중단되었는지 확인한 다음 다음 판단을 내립니다. 여기서 우리는 주로 두 번째 판단을 살펴 봅니다.
보호 된 int tryacquireshared (int acquises) {return (getState () == 0)? 1 : -1;}Tryacquireshared 방법은 동기화로 구현됩니다.
AbstractQueuedSynchronizer에는 IT의 구현이 있지만 기본 구현은 예외를 던지는 것입니다.
TREICQUIRSHARED이 방법은 현재 객체의 상태가 잠금을 얻을 수 있는지 여부를 쿼리하는 데 사용됩니다.
우리는 state가 0인지 결정하여 해당 int 값을 반환한다는 것을 알 수 있습니다.
그래서 국가는 무엇을 의미합니까?
/*** 동기화 상태. */ 개인 휘발성 int 상태;
위의 코드는 상태가 동기화 상태를 나타냅니다.
상태는 휘발성 키워드를 사용하여 수정합니다.
휘발성 키워드는 상태 수정이 즉시 기본 메모리로 업데이트되도록 할 수 있습니다. 다른 스레드를 읽어야 할 때 새 값은 메모리에서 읽습니다.
즉, 국가의 가시성이 보장됩니다. 최신 데이터입니다.
여기에 오는 상태는 무엇입니까?
여기서 우리는 CountdownLatch의 생성자를 살펴 봐야합니다.
CountdownLatch End = New CountdownLatch (2); ↓ public countdownlatch (int count) {if (count <0) 새로운 불법 불법 행위 ( "count <0"); this.sync = new sync (count);} ↓ sync (int count) {setstate (count);}생성자의 숫자는 상태를 설정하는 데 사용됩니다.
그래서 우리는 여기에 state == 2가 있습니다. TREICQUIRSHARED는 -1을 반환합니다. 아래에 입력하십시오
doacquiresharedinterpruptility (arg); ↓ 개인 void doacquiresharedinterpruptibly (int arg)는 InterruptedException {최종 노드 노드 = addwaiter (node.shared); 부울 실패 = 참으로; try {for (;;) {최종 노드 p = node.predecessor (); if (p == head) {int r = tryacquireshared (arg); if (r> = 0) {SetheadandPropagate (Node, R); p.next = null; // gc 실패 = false; 반품; }} if (whiteparkfterfailedacquire (p, node) && parkandcheckinterrupt ()) 던질 새로운 InterruptedException (); }} 마침내 {if (실패) cancelAcquire (노드); }}좋아,이 코드는 약간 길고 여러 기능이 호출됩니다. 하나씩 보자.
첫 번째 줄에 새로운 클래스 노드가 나타납니다.
노드는 AQS (AbstractQueuedSynchronizer) 클래스의 내부 클래스로 체인 구조를 정의합니다. 아래와 같이.
+------+prev+-----++-----+헤드 | | <---- | | <---- | | | 꼬리 +----- + +----- + +----- +
이 구조를 기억하십시오.
코드 addwaiter (node.shared)의 첫 번째 줄에는 메소드가 있습니다.
addwaiter (node.shared) //node.shared는 노드가 공유 모드에 있음을 의미합니다. ↓ 개인 노드 Addwaiter (Node Mode) {Node Node = new Node (Thread.CurrentThread (), Mode); // ENQ의 빠른 경로를 시도하십시오. 실패 노드에서 전체 ENQ로 백업하면 Pred = Tail; // 개인 과도 휘발성 노드 꼬리; if (pred! = null) {node.prev = pred; if (compareAndsettail (pred, node)) {pred.next = 노드; 리턴 노드; }} enq (노드); 반환 노드;}먼저 노드가 구성되고 현재 스레드가 저장됩니다. 모드는 공유 모드입니다.
꼬리는 대기 대기열의 대기열 끝 이이 순간에 무효임을 의미합니다. 따라서 pred == null은 enq (노드)로 들어갑니다.
enq (노드) ↓ 개인 노드 enq (최종 노드 노드) {for (;;) {node t = tail; if (t == null) {// if (compareAndsethead (new node ())) tail = head; } else {node.prev = t; if (compareAndsettail (t, node)) {t.next = 노드; 반환 t; }}}}같은 꼬리는 null이고 비교를 입력하십시오.
CompareAndSethead (new Node ()) ↓/*** CAS 헤드 필드. ENQ에 의해서만 사용됩니다. */Private Final Boolean CompareAndSethead (노드 업데이트) {return insafe.compareAndswapoBject (this, 헤드 오프셋, null, 업데이트);}이것은 CAS 작업입니다. 헤드가 NULL 인 경우 대기 대기열의 헤드가 새 노드 인 업데이트 값으로 설정됩니다.
꼬리 = 머리; 그런 다음 현재 꼬리는 더 이상 무효가되지 않습니다. 다음주기를 입력하십시오.
이번에는 먼저 노드의 이전 포인터를 꼬리로 가리킨 다음 CAS 작업을 통해 노드를 꼬리로 설정하고 큐의 꼬리, 즉 노드를 반환합니다.
대기 대기열의 모델은 다음과 같이 변경됩니다
+ ------+ 이전 +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
좋아, 여기에 도착하면 기다려주는 메소드가 반환되면 현재 스레드의 노드와 동일한 스레드입니다.
doacquiresharedinterpruptily (int arg)로 돌아가 다음 루프를 입력하십시오.
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보다 크다고 가정하면 현재 R <0이므로 DooksKaffterfailedAcquire 메소드를 입력하십시오.
whildparkfterfailedacquire (p, 노드) ↓ 개인 정적 부울 꼭 봐야합니다. if (ws == node.signal) // 정적 최종 int 신호 = -1; / * *이 노드는 이미 릴리스를 요청하는 상태를 설정하여 신호를 보내므로 안전하게 주차 할 수 있습니다. */ Return True; if (ws> 0) { / * * 전임자가 취소되었습니다. 전임자를 건너 뛰고 * 재시도를 표시하십시오. */ do {node.prev = pred = pred.prev; } while (pred.waitstatus> 0); pred.next = 노드; } else { / * * waitstatus는 0이거나 전파해야합니다. 우리는 신호가 필요하지만 아직 주차하지 않음을 나타냅니다. 발신자는 주차하기 전에 획득 할 수 없는지 확인하기 위해 재 시도해야합니다. */ compareAndsetwaitstatus (pred, ws, node.signal); } return false;} ↓/*** 노드의 CAS Waitstatus 필드. */private static final boolean boolean boolean compareandsetwaitstatus (노드 노드, int eppler, int update) {return unsafe.compareandswapint (node, waitstatusoffset, expling, update);}Dooksparkfterfailedacquire도 CompareNdestwaitStatus로가는 길을 볼 수 있습니다.
CompareAndsetwaitstatus 이전의 웨이터를 Node.signal로 설정했습니다.
node.signal은 후속 노드의 스레드가 파킹되지 않아야 함을 의미합니다 (깨어나는 것과 유사). 이 메소드는 False를 반환합니다.
이주기 후 큐 모델은 다음 상태가됩니다.
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
DSTPARKAFTERFAILEDACQUIRE는 False를 반환하기 때문에 더 이상 다음 조건을 보지 않습니다. (;;)를 위해 루프를 계속하십시오.
상태가 여전히 0보다 큰 경우 다시 닥칠 파크 래프터 파일로드에 들어갑니다.
이번에는 머리의 웨이터 타투스가 node.signal이기 때문에 꼭 봐야합니다.
이번에는 ParkandCheckinterrupt 메소드를 봐야합니다.
Private Final Boolean ParkandCheckinterRup () {Locksupport.park (this); return shread.interrupted (); }자, 스레드가 중단되지 않으므로 False를 반환하십시오. (;;)를 위해 루프를 계속하십시오.
상태가 항상 0보다 크고 스레드가 중단되지 않으면 항상이 루프에 있습니다. 즉, 이전 기사에서 언급 한 심판은 항상 게임의 끝을 발표하기를 꺼려했습니다.
그래서 어떤 상황에서 루프가 나올 것인가? 즉, 어떤 상황에서 어떤 상황에서 0보다 작을 것인가? 다음 기사를 설명하겠습니다.
요약하면, AWAIT () 메소드는 실제로 대기열을 초기화하고 대기 해야하는 스레드 (state> 0)를 대기열에 추가하고 웨이터스를 사용하여 후속 노드의 스레드 상태를 표시하는 것입니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.