이전 세 기사의 분석을 통해 우리는 AbstractQueuedSynchronizer의 내부 구조와 일부 설계 개념에 대한 깊은 이해를 가지고 있으며, AbstractQueuedSynchronizer는 동기화 상태와 두 개의 큐 영역을 각각 동기 대기열과 조건부 큐인을 유지한다는 것을 알고 있습니다. 공중 화장실을 비유로 사용합시다. 동기화 큐는 기본 대기열 영역입니다. 공중 화장실이 열려 있지 않으면 화장실에 들어가기를 원하는 모든 사람은 여기에서 대기해야합니다. 조건 큐는 주로 대기 대기로 설정됩니다. 사람이 마침내 자물쇠를 성공적으로 얻고 대기열을 통해 화장실에 들어가면 편의 전에 화장지를 가져 오지 않는다고 생각한다고 상상해 봅시다. 그는이 상황에 직면 할 때 무력하지만이 사실을 받아 들여야합니다. 현재로서는 화장지를 먼저 준비해야합니다 (대기 조건 대기열을 입력하십시오). 물론 외출하기 전에 다른 사람들이 들어올 수 있도록 자물쇠를 풀어야합니다. 화장지를 준비한 후 (조건이 충족됨) 동기 대기열로 돌아가서 다시 대기열로 돌아와야합니다. 물론 방에 들어가는 모든 사람들이 화장지를 가져 오지는 않았습니다. 작업을 방해하고 조건 대기열에서 대기열을 위로 올리는 다른 이유가있을 수 있습니다. 따라서 여러 조건 대기열이있을 수 있으며 다른 대기 조건에 따라 다른 조건 대기열이 설정됩니다. 조건 대기열은 일방 통행 링크 목록입니다. 조건 인터페이스는 조건 큐의 모든 작업을 정의합니다. AbstractQueuedSynchronizer 내부의 ConditionObject 클래스는 조건 인터페이스를 구현합니다. 조건 인터페이스에서 어떤 작업이 정의되는지 살펴 보겠습니다.
공개 인터페이스 조건 {// 스레드 인터럽트에 대한 응답 대기중인 void await ()가 InterpruptedException을 던집니다. // 스레드 인터럽트에 응답하지 않기를 기다리고 있습니다. // 상대 시간 대기 대기 조건 (스핀 없음) 긴 awaitnanos (Long nanostimeout)는 중단 된 예를 던집니다. // 상대 시간 대기 대기 조건 (스핀) 부울이 기다리고 있습니다 (오랜 시간, 시간대)는 중단 된 예를 던집니다. // 절대 시간 대기 대기 조건 설정 부울 awaituntil (날짜 마감일)은 InterruptedException을 던집니다. // 조건 큐에서 헤드 노드 void Signal ()을 깨우십시오. // 조건 큐 void signallall ()의 모든 노드를 깨우십시오. }조건 인터페이스는 너무 많은 방법을 정의하지만 총 두 가지 범주로 나뉩니다. 기다리는 방법은 스레드가 조건 큐에 들어가서 대기하는 메소드이며, 신호로 시작하는 메소드는 조건 큐의 스레드를 "깨우는"메소드입니다. 여기서 신호 방법을 호출하면 스레드를 깨우거나 깨우지 않을 수도 있습니다. 스레드가 깨어날 때 나중에 논의 될 바와 같이 상황에 따라 다르지만 신호 메소드를 호출하면 스레드가 조건부 큐에서 동기화 큐의 꼬리로 분명히 움직일 것입니다. 나레이션 편의를 위해서는 당분간에 대해 걱정하지 않을 것입니다. 신호 메소드를 웨이크 업 조건부 큐 스레드의 작동이라고합니다. 5 가지 유형의 기다려온 방법, 즉 응답 스레드 인터럽트 대기, 비 응답 스레드 인터럽트 대기, 비 스핀 대기 대기, 상대 시간 스핀 대기 및 절대 시간 대기 시간이 있습니다. 신호 방법에는 두 가지 유형의 신호 메소드가 있습니다. 즉, 조건 큐 헤드 노드를 깨우고 조건 큐의 모든 노드를 깨우는 것만이 있습니다. 동일한 유형의 방법은 기본적으로 동일합니다. 공간 제한으로 인해 불가능하며 이러한 방법에 대해 신중하게 이야기 할 필요는 없습니다. 우리는 하나의 대표 방법을 이해 한 다음 다른 방법을 이해해야합니다. 따라서이 기사에서는 Await 메소드 및 신호 메소드에 대해서만 자세히 이야기 할 것입니다. 다른 방법에 대해 자세히 설명하지는 않지만 참조에 대한 소스 코드를 게시합니다.
1. 스레드 인터럽트 조건에 대한 응답을 기다립니다.
// 스레드 인터럽트의 조건에 대한 응답으로 대기 대기 대중 공개 최종 void await ()가 인터럽트 exception {// 스레드가 중단되면 (thread.interrupted ()) {throw new InterruptedException (); } // 조건 큐 노드 노드의 꼬리에 현재 스레드를 추가합니다 = addConditionWaiter (); // 조건을 입력하기 전에 잠금을 풀 릴리스 대기 int int savedstate = fullerrelease (노드); int 인터럽트 모드 = 0; // 스레드는 조건부로 While Loop에서 대기 중입니다. 동기화 큐의 전방 노드가 취소되었습니다 // 2. 동기화 큐의 전방 노드 상태를 신호 실패 // 3으로 설정하십시오. 전방 노드가 잠금을 방출 한 후 현재 노드가 깨어납니다. // 현재 스레드는 즉시 중단되는지 확인합니다. 그렇다면 노드가 대기 대기 조건을 취소 함을 의미합니다. 이 시점에서 ((인터럽트 모드 = checkinterruptwhileWaiting (node)) {break; }} // 스레드가 깨어 난 후 (AcquireQueued (Node, SavedState) && 인터럽트 모드! = THRISH_IE) {인터럽트 모드 = reenterprupt; } //이 작업은 주로 스레드가 신호 전에 방해되는 것을 방지하기 위해 (node.nextWaiter! = null) if (node.nextwaiter! = null) if 조건 큐에서 연결을 끊지 않습니다. } // 인터럽트 모드에 응답하는 인터럽트 처리 (인터럽트 모드! = 0) {reportInterRupTAPTERWAIT (인터럽트 모드); }}스레드가 대기 방법을 호출하면 현재 스레드는 노드 노드로 랩핑되어 조건 큐의 꼬리에 배치됩니다. AddConditionWaiter 메소드에서 조건 큐 엔드 노드가 취소되는 것으로 밝혀지면 UnlinkCancelledwaiters 메소드가 호출되어 조건 큐의 모든 취소 된 노드를 지 웁니다. 이 단계는 노드 삽입 준비입니다. 테일 노드의 상태가 조건인지 확인한 후, 현재 스레드를 감싸고 조건 큐의 꼬리에 넣기 위해 새 노드가 생성됩니다. 이 프로세스는 스레드를 중단하지 않고 동기화 큐의 꼬리에 노드를 추가합니다.
2 단계 : 잠금을 완전히 해제하십시오
// 풀 릴리스 잠금 최종 int ultrelease (노드 노드) {부울 실패 = true; try {// 현재 동기화 상태를 가져옵니다. int savedstate = getState (); // 현재 동기화 상태를 사용하여 잠금 IF (release (savedState)) {실패 = false; // 잠금이 성공적으로 릴리스되면 SavedState를 반환하십시오. } else {// 잠금이 릴리스되면 런타임 예외를 던지면 새로운 불법 모니터 스테이트 exception ()을 던지십시오. }} 마지막으로 {// 노드가 (실패) {node.waitstatus = node.cancelled 인 경우 취소 상태로 설정되어 있는지 확인합니다. }}}전류 스레드를 노드로 감고 조건 큐의 꼬리에 추가 한 후에는 완전 릴리스 방법을 호출하여 잠금을 해제합니다. FullerRelease라는 방법은 자물쇠가 재진입되어 있기 때문에 잠금 장치를 완전히 해제하는 데 사용되므로 조건부 대기 전에 잠금 장치를 해제해야합니다. 그렇지 않으면 다른 사람들은 잠금을 얻을 수 없습니다. 잠금이 해제되면 런타임 예외가 발생합니다. 잠금이 성공적으로 해제되면 이전 동기화 상태로 돌아갑니다.
3 단계 : 조건을 기다립니다
// 스레드가 while 루프에서 대기 중이 었습니다. 스레드가 깨어나는 경우가 몇 가지 있습니다 : // 1. 동기화 큐의 전방 노드가 취소되었습니다 // 2. 동기화 큐의 전방 노드 상태를 신호 실패 // 3으로 설정하십시오. 전방 노드가 잠금을 방출 한 후 현재 노드가 깨어납니다. Locksupport.park (this); // 현재 스레드가 즉시 깨어나서 중단되었는지 확인합니다. 그렇다면 노드가 대기 대기 조건을 취소 함을 의미합니다. 이 시점에서 ((인터럽트 모드 = checkinterruptwhileWaiting (node)) {break; }} // 상태 대기 상태에서 대기하는 상태에서 스레드 인터럽트 상황을 확인하십시오. 개인 int checkinterruptwhilewaiting (노드 노드) {// 인터럽트 요청은 신호 작동 전에 다음과 같습니다. Throw_ie // 인터럽트 요청은 신호 작업 후에 다음과 같습니다.이 기간 동안 인터럽트 요청이 수신되지 않았습니다. (TransferCancelledwait (Node)? trash_ie : reterrupt) : 0;} // 조건 큐에서 대기 대기 대기 조건을 동기 큐에서 대기하는 조건을 동기화하는 노드 노드 (Node Node) {//이 CAS 작동이 성공적이면 (Node가 Node) (Node)가 발생한다는 것을 의미합니다. node.condition, 0)) {// 상태 수정이 성공한 후 노드를 동기화 큐 ENQ (노드)의 꼬리에 넣습니다. 진실을 반환하십시오. } // 이것은 CAS 작동에 실패했음을 나타냅니다. (! isonsyncqueue (node)) {// Sinal 메소드가 동기화 큐로 전송되지 않은 경우, stread.yield ()를 기다리십시오. } return false;}위의 두 작업이 완료되면 while 루프가 들어갑니다. while 루프가 먼저 Locksupport.park (this)를 호출하여 스레드를 걸어서 항상 차단 될 것임을 알 수 있습니다. 신호 메소드를 호출 한 후 조건부 큐에서 동기화 큐로 노드를 전송하십시오. 스레드가 깨어날 지 여부는 상황에 따라 다릅니다. 노드를 전송할 때 동기화 큐의 전방 노드가 취소되거나 전방 노드의 상태가 신호 실패로 업데이트 된 경우 두 경우 모두 스레드가 즉시 깨어납니다. 그렇지 않으면, 이미 동기화 큐에있는 스레드는 신호 메소드의 끝에서 깨어나지 않지만 전방 노드가 일어날 때까지 기다립니다. 물론, 신호 메소드를 호출하여 깨어나는 것 외에도 스레드는 인터럽트에 응답 할 수도 있습니다. 스레드가 여기서 인터럽트 요청을 받으면 계속 실행됩니다. 스레드가 깨어 난 후에는 인터럽트 또는 신호 메소드를 통해 깨어 있는지 즉시 확인할 수 있습니다. 인터럽트로 깨어나면이 노드를 동기화 큐로 전송하지만 TransferCancelledwait 메소드를 호출하여 달성됩니다. 이 단계의 최종 실행 후, 인터럽트가 반환되고 while 루프가 튀어 나옵니다.
4 단계 : 조건 큐에서 노드가 제거 된 후 작동
// 스레드가 깨어나면 (arcirequeued (node, savedstate) && 인터럽트 모드! = strash_ie) {인터럽트 모드 = rementrupt;} //이 작업은 스레드가 신호 전 방해를 방지하고 (node.nextwaiter! = null) {null)과의 접촉을 유발하지 않도록하는 경우 독점 모드에서 잠금을 획득합니다. UnlinkCancelledwaiters ();} // 인터럽트 모드에 응답하는 인터럽트 처리 처리 (인터럽트 모드! = 0) {reportInterRuptAfterwait (인터럽트 모드);} // 조건을 종료 한 후 인터럽트 avoid보고 ReportIrpruptAfterWait (Int InterruptMode)를 기반으로 해당 처리가 발생합니다. if (interruptMode == trash_ie) {trash new InterruptedException (); // 인터럽트 모드가 재 해석되면 자체적으로 매달려} else if (interruptMode == reterrupt) {selfinterrupt (); }}스레드가 while 루프, 즉 조건이 대기되는 경우 동기화 큐로 돌아갑니다. 신호 메소드를 다시 호출하거나 스레드 인터럽트로 인해 노드는 결국 동기 큐에 있습니다. 현재 획득 방법은 동기화 큐에서 잠금을 획득하는 작업을 수행하도록 호출됩니다. 우리는 이미 독점 모드 기사 에서이 방법에 대해 자세히 논의했습니다. 다시 말해, 노드가 조건 대기열에서 나온 후에는 순종적으로 독점 모드의 잠금 세트로 이동합니다. 이 노드가 잠금을 다시 획득 한 후에는이 기간 동안 인터럽트 상황에 따라 reportInterRUPTAFTERWAIT 메소드를 호출합니다. 신호 메소드 전에 인터럽트가 발생하면 인터럽트 모드가 strash_ie이며 잠금이 다시 얻은 후에 예외가 발생합니다. 신호 메소드 후 인터럽트가 발생하면 인터럽트 모드가 재 해석되며 잠금이 다시 얻은 후에 다시 중단됩니다.
2. 스레드 인터럽트에 대한 비 응답을 기다리고 있습니다
// 공개 최종 void 대기 awaitUnInterRUPTILY () {// 조건 큐 노드 노드의 꼬리에 현재 스레드를 추가합니다. = addConditionWaiter (); // 전체 릴리스 잠금 장치 및 전류 동기화 상태 int SavedState = FullerRelease (Node); 부울 중단 = 거짓; // 노드가 조건부로 While Loop에서 대기 중입니다. // 스레드가 깨어나서 (thread.interrupted ()) {interrupted = true; }} if (aquirequeued (node, savedstate) || 인터럽트) {// 여기에서 모든 인터럽트 요청에 응답합니다. 다음 두 조건 중 하나가 충족되면 자체적으로 매달려 있습니다. 스레드는 조건이 대기하는 동안 인터럽트 요청을받습니다. 스레드는 AcquireQueued Method Selfinterrupt ()에서 인터럽트 요청을 수신합니다. }}3. 상대 시간 조건 대기 (스핀 없음) 설정
// 타이밍 조건 대기 (상대 시간)를 설정하고 스핀 대기 대기 대기 최종 최종 최종 Long Awaitnanos (Long Nanostimeout)가 중단 exception 던지기 {// 스레드가 중단되면 (Thread.interrupted ()) {throw new InterruptedException (); } // 조건 큐 노드 노드의 꼬리에 현재 스레드를 추가합니다 = addConditionWaiter (); // 완전 릴리스 조건을 입력하기 전에 잠금 장치를 대기하기 전에 int savedstate = FullerRelease (노드); 마지막 마지막 = System.NanoTime (); int 인터럽트 모드 = 0; while (! isonsyncqueue (node)) {// 타임 아웃이 사용되는지 (nanostimeout <= 0l) {// 타임 아웃이 완료된 경우 대기 조건을 실행해야합니다. 부서지다; } // 현재 스레드를 일정 시간 동안 걸면이 기간 동안 스레드가 깨어날 수 있습니다. // 스레드가 깨어 난 후 먼저 인터럽트 정보를 확인하십시오 (((인터럽트 모드 = checkinterruptwhileWaiting (node))! = 0) {break; } long now = system.nanotime (); // 시간 초과 시간 마이너스 조건 Nanostimeout- = 지금 - 마지막; 마지막 시간 = 지금; } // 스레드가 깨어 난 후 (arcirequeued (node, savedstate) && 인터럽트 모드! = strash_ie) {interpruptMode = reenterprupt; } // TransferCancelledWait 메소드가 NextWaiter를 비우지 않기 때문에 (node.nextWaiter! = null) {UnlinkCancelledwaiters (); } // 인터럽트 모드에 응답하는 인터럽트 처리 (인터럽트 모드! = 0) {reportInterRupTAPTERWAIT (인터럽트 모드); } // 나머지 시간을 반환합니다 Nanostimeout- (System.NanoTime () - 마지막 시간);}4. 상대 시간 조건 대기 (스핀) 설정
// 시간이 지정된 조건 대기 (상대 시간), 스핀 대기 대기 공개 최종 부울을 수행합니다 (긴 시간, TimeUnit Unit)는 중단 exception {if (init == null) {throw nullpointerException (); } // 타임 아웃의 밀리 초를 Long Nanostimeout = init.tono (time); // 스레드가 중단되면 (Thread.terrupted ()) {throw new InterruptedException (); } // 조건 큐 노드 노드의 꼬리에 현재 스레드를 추가합니다 = addConditionWaiter (); // int savedstate = fullerrelease (node)를 대기하기 위해 조건을 입력하기 전에 잠금을 풀어 놓습니다. // 현재 시간의 밀리 초를 마지막으로 가져옵니다. 마지막 시간 = System.NanoTime (); 부울 타임 아웃 = 거짓; int 인터럽트 모드 = 0; while (! isonsyncqueue (node)) {// 타임 아웃이 시간 초과 인 경우 (nanostimeout <= 0l) {timedout = transferAfterCelledwait (node) 인 경우 대기 조작 취소 조건을 수행해야합니다. 부서지다; } // 타임 아웃 시간이 스핀 타임보다 큰 경우 (nanostimeout> = spinfortimeoutthreshold) {locksupport.parknanos (this, nanostimeout); } // 스레드가 깨어 난 후 먼저 인터럽트 정보를 확인합니다. } long now = system.nanotime (); // 시간마다 시간마다 시간을 빼고 대기 대기 시간의 시간을 빼냅니다 - = 지금 - 마지막 시간; 마지막 시간 = 지금; } // 스레드가 깨어 난 후 (arcirequeued (node, savedstate) && 인터럽트 모드! = strash_ie) {interpruptMode = reenterprupt; } // TransferCancelledWait 메소드가 NextWaiter를 비우지 않기 때문에 (node.nextWaiter! = null) {UnlinkCancelledwaiters (); } // 인터럽트 모드에 응답하는 인터럽트 처리 (인터럽트 모드! = 0) {reportInterRupTAPTERWAIT (인터럽트 모드); } // 타임 아웃 플래그가 반환 되든 반환! 시간 초과;}5. 절대 시간 조건 대기를 설정하십시오
// 시간이 지정된 조건 대기 (절대 시간) 공개 최종 부울 aweaituntil (날짜 마감일)은 중단 된 결과 {if (deadline == null) {throw new nullpointerexception (); } // 절대 시간의 밀리 초를 가져옵니다. // 스레드가 중단되면 (Thread.terrupted ()) {throw new InterruptedException (); } // 조건 큐 노드 노드의 꼬리에 현재 스레드를 추가합니다 = addConditionWaiter (); // 조건을 입력하기 전에 잠금을 풀 릴리스 대기 int int savedstate = fullerrelease (노드); 부울 타임 아웃 = 거짓; int 인터럽트 모드 = 0; while (! isonsyncqueue (node)) {// 타임 아웃 인 경우 (System.CurrentTimeMillis ()> abstime) {timedout = TransferAfterCelledwait (Node); 부서지다; } // 스레드를 일정 시간 동안 걸어서 실이 깨어날 수 있거나 그 자체로 깨어날 시간이 될 수 있습니다. // 스레드가 깨어 난 후 먼저 인터럽트 정보를 확인합니다 (((인터럽트 모드 = checkinterruptwhileWaiting (node))! = 0) {break; }} // 스레드가 깨어 난 후 (AcquireQueued (Node, SavedState) && 인터럽트 모드! = THRISH_IE) {인터럽트 모드 = reenterprupt; } // TransferCancelledWait 메소드가 NextWaiter를 비우지 않기 때문에 (node.nextWaiter! = null) {UnlinkCancelledwaiters (); } // 인터럽트 모드에 응답하는 인터럽트 처리 (인터럽트 모드! = 0) {reportInterRupTAPTERWAIT (인터럽트 모드); } // 타임 아웃 플래그가 반환 되든 반환! 시간 초과;}6. 조건부 대기열에서 헤드 노드를 깨우십시오.
// 조건에서 다음 노드를 깨우십시오 큐 공개 Final void Signal () {// 현재 스레드가 잠금을 유지하는지 여부를 판단하십시오 (! isheldexClusially (! } node first = firstwaiter; // 조건 큐에 큐가있는 경우 (첫 번째! = null) {// 조건 큐 doSignal (첫 번째)에서 헤드 노드를 깨우십시오. }} // 컨디션 큐에서 헤드 노드를 깨우십시오 비공개 void dosignal (Node First) {do {// 1. FirstWaiter 참조를 하나씩 if ((firstwaiter = first.nextwaiter) == null) {lastwaiter = null; } // 2. 헤드 노드의 후속 노드의 참조를 먼저 비우십시오 .nextwaiter = null; // 3. 헤드 노드를 동기화 큐로 전송하면 전송이 완료된 후 실을 깨울 수 있습니다. TransforforSignal 작동이 실패하면 다음 노드를 깨우십시오} while (! transforforsignal (first) && (first = firstwaiter)! = null);} // 조건 큐에서 동기화 큐에서 지정된 노드를 동기 큐에서 전송 최종 부울 전 TransforSignal (Node Node)으로 전송하십시오. node.condition, 0)) {// 상태를 업데이트하는 작업이 실패하면 직접 False를 반환합니다. // TransferAfterCancelledwait 메소드가 상태를 먼저 변경 하여이 CAS 작업이 잘못 실패하게 될 수 있습니다. } //이 노드를 동기화 큐 노드의 꼬리에 추가합니다. p = enq (노드); int ws = p.waitstatus; if (ws> 0 ||! compareAndsetwaitstatus (p, ws, node.signal)) {// 다음 상황이 발생하면 // 1. 전방 노드는 취소 상태 // 2에 있습니다. 업데이트 노드의 상태는 신호 작동 실패 LocksUpport.unpark (node.thread)입니다. } return true;}신호 방법의 궁극적 인 핵심은 TransforforSignal 메소드를 호출하는 것임을 알 수 있습니다. TransforforSignal 메소드에서 먼저 CAS 작업을 사용하여 노드의 상태를 조건에서 0으로 설정 한 다음 ENQ 메소드를 호출하여 동기화 큐의 꼬리에 노드를 추가하십시오. 다음 IF 판결 진술을 볼 수 있습니다. 이 판단 진술은 주로 스레드가 언제 깨어날지를 결정하는 데 사용됩니다. 이 두 상황이 발생하면 실이 즉시 깨어납니다. 하나는 이전 노드의 상태가 취소되고 다른 하나는 이전 노드의 상태가 업데이트되지 않은 경우입니다. 두 경우 모두 스레드가 즉시 깨어납니다. 그렇지 않으면 조건부 대기열에서 동기화 큐로 노드를 전송하여 노드의 스레드가 즉시 깨어나지 않습니다. SignalAll 방법은 조건부 큐의 모든 노드를 통해 루프를 통해 동기 큐로 전송한다는 점을 제외하고는 대략 유사합니다. 노드를 전송하는 방법은 여전히 TransforForsignal 메소드를 호출합니다.
7. 조건 대기열의 모든 노드를 깨우십시오
// 조건 큐 뒤에있는 모든 노드를 깨우십시오. 공개 최종 void signallall () {// 현재 스레드가 잠금을 유지하는지 (! isheldexclusially (! } // 컨디션 큐 헤더 노드 노드 먼저 = FirstWaiter; if (first! = null) {// 조건 대기열의 모든 노드 DoSignalall (첫 번째); }} // 조건 큐의 모든 노드를 깨우십시오 큐 개인 void doSignalall (Node First) {// 먼저 헤더 노드의 참조 및 테일 노드 마지막 atrestwaiter = firstwaiter = null; 수행 {// 후속 노드의 참조를 먼저 받으십시오 = First.nextWaiter; // 먼저 전송할 노드의 후속 참조를 비우십시오 .nextWaiter = null; // 조건부 큐에서 동기화 큐로 노드를 전송합니다. // 다음 노드에 대한 참조를 먼저 가리키십시오 = 다음; } while (첫 번째! = null);}이 시점에서 우리의 전체 AbstractQueuedSynchronizer 소스 코드 분석은 끝났습니다. 나는이 네 가지 분석을 통해 모든 사람이 AQ를 더 잘 마스터하고 이해할 수 있다고 생각합니다. 이 범주는 다른 많은 동기화 범주의 초석이기 때문에 실제로 매우 중요합니다. 저자의 제한된 수준과 표현 능력으로 인해 명확한 진술이나 부적절한 이해가 없다면 제 시간에 수정하고 함께 논의하고 배우십시오. 아래의 문제를 읽기 위해 메시지를 남길 수 있습니다. AQS 주석 소스 코드가 필요한 경우 저자에게 연락하여 요청할 수도 있습니다.
참고 : 위의 모든 분석은 JDK1.7을 기반으로하며 다른 버전간에 차이가있을 것이므로 독자는주의를 기울여야합니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.