이전 기사의 분석을 통해 우리는 독점 모드로 잠금을 획득하는 세 가지 방법, 즉 응답 스레드 인터럽트없이, 응답 스레드 인터럽트를 얻고 시간 초과 시간을 얻는 세 가지 방법이 있음을 알고 있습니다. 공유 모드에서 자물쇠를 획득하는 세 가지 방법이 있으며 기본적으로 동일합니다. 우리가 한 가지 방법을 알아 내면 다른 방법을 빠르게 이해할 수 있습니다. AbstractQueuedSynchronizer 소스 코드는 천 줄 이상을 가지고 있지만, 여러 번 반복되므로 독자는 처음에 무서워해서는 안됩니다. 참을성 있고 천천히 읽으면 자연스럽게 점차 이해하게됩니다. 내 개인적인 경험에는 AbstractQueuedSynchronizer 소스 코드를 읽을 때 이해해야 할 몇 가지 중요한 측면이 있습니다. 즉 독점 모드와 공유 모드의 차이, 노드의 대기 상태 및 조건부 큐의 이해가 있습니다. 이러한 핵심 요점을 이해하면 후속 소스 코드 판독 값이 훨씬 쉬워집니다. 물론, 이것들은 나의 기사 "Java Concurrency Series [1] ---- AbstractQueuedSynchronizer 소스 코드 분석"에 소개되며 독자는 먼저 확인할 수 있습니다. 이 기사는 공유 모드를 잠금 장치를 획득하는 세 가지 방법과 잠금 장치를 해제하는 한 가지 방법으로 분석합니다.
1. 스레드 인터럽트 획득에 응답하지 않습니다
// 호기식 모드에서 잠금을 획득 (공유 모드) 공개 최종 void aquireshared (int arg) {// 1. (tryacquireshared (arg) <0) {// 2. 취득이 실패하면이 방법을 입력하십시오. Doacquireshared (arg); }} // 잠금 (공유 모드)을 획득하려고 시도하십시오 (공유 모드) // 음수 숫자 : 획득 실패 // Zero 값이 실패했음을 나타냅니다. // Zero 값 : 현재 노드가 성공적으로 획득되었지만 후속 노드가 더 이상 획득 할 수 없음을 나타냅니다.획득 방법을 호출하는 것은 스레드 인터럽트에 응답하지 않고 잠금을 획득하는 방법입니다. 이 방법에서 TreeAcquireshared는 먼저 잠금 장치를 얻으려고 요청됩니다. TreeAcquireshared 방법은 잠금을 획득하는 상태를 반환합니다. 여기서 AQS는 리턴 상태가 음수 인 경우 현재 노드가 잠금을 획득하지 않음을 의미합니다. 0이면 현재 노드가 잠금을 획득하지만 후속 노드는 더 이상 획득 할 수 없다는 것을 의미합니다. 양수이면 현재 노드가 잠금을 획득 하고이 잠금의 후속 노드도 성공적으로 얻을 수 있음을 의미합니다. 서브 클래스가 Tryacquireshared 방법에 의해 잠금을 얻는 논리를 구현하면 반환 값은이 협약을 준수해야합니다. TryAcquireshared를 호출하는 반환 값이 0보다 작 으면 잠금을 얻으려는 시도가 실패했음을 의미합니다. 다음으로 Doacquireshared 방법을 호출하여 현재 스레드를 동기화 큐에 추가하십시오. 우리는 doacquireshared 방법을 봅니다.
// 동기화 큐에서 얻기 (공유 모드) get private void doacquireshared (int arg) {// 동기화 큐에 추가 최종 노드 노드 = addwaiter (node.shared); 부울 실패 = 참으로; {boolean Interrupted = false; for (;;) {// 현재 노드 최종 노드의 전방 노드를 가져옵니다. p = node.predecessor (); // 전방 노드가 헤드 노드 인 경우 (p == head) {// 획득 상태를 다시 획득하고 획득 상태를 반환하려고 시도하면 획득이 실패했음을 나타냅니다. 획득 된 int r = tryacquireshared (arg); if (r> = 0) {//이를 위해 현재 노드가 잠금을 성공적으로 획득했음을 나타냅니다. 이때, 그것은 잠금 상태 정보를 후속 노드 세 테드 랜드 프로파이트 (Node, r)로 전파 할 것이다. p.next = null; // 스레드 차단 중에 인터럽트 요청이 수신되면 (Interprupted) {selfinterrupt (); } 실패 = 거짓; 반품; }} // 잠금 획득이 실패 할 때마다 스레드가 매달릴 수 있는지 여부가 결정됩니다. 가능하면 (DishparkAfterfailedAcquire (p, node) && parkandcheckinterrupt ()) {interprupted = true; }}} 마침내 {if (실패) {cancelAcquire (노드); }}}Doacquireshared 메소드를 먼저 입력하려면 Addwaiter 메소드를 호출하여 현재 스레드를 노드로 랩핑하여 동기화 큐의 끝에 넣습니다. 우리는 독점 모드에 대해 이야기 할 때 노드를 추가하는 과정에 대해 이야기 했으므로 여기서는 이야기하지 않습니다. 노드가 동기화 큐에 들어간 후, 헤드 노드의 스레드가 잠금을 획득하고 방에 들어갔 기 때문에 앞쪽의 노드가 헤드 노드라는 것을 알게되면 잠금을 얻을 차례입니다. 따라서 현재 노드는 먼저 자체적으로 매달리지 않지만 잠금을 다시 얻으려고합니다. 앞에있는 사람이 잠금 장치와 잎을 풀면 현재 노드가 잠금을 성공적으로 얻을 수 있습니다. 앞에있는 사람이 자물쇠를 공개하지 않은 경우 DookskaFatterFailedAcquire 방법을 호출합니다. 이 방법에서는 헤드 노드의 상태가 신호로 변경됩니다. 이전 노드의 상태가 신호임을 확인함으로써 만 현재 노드는 자신감과 함께 매달릴 수 있습니다. 모든 스레드는 ParkandCheckinterrupt 메소드에 매달됩니다. 전류 노드가 잠금을 성공적으로 획득하면 SetheadandPropagate 메소드가 호출되어 헤드 노드로 설정하고 공유 모드 인 노드를 깨우십시오. SetheadandPropagate 방법의 특정 작동을 살펴 보겠습니다.
// 헤드 노드를 설정하고 잠금 상태 (공유 모드) 개인 void setheadandPropagate (노드 노드, int propagate) {node h = head; // 주어진 노드를 헤드 노드 Sethead (노드)로 설정합니다. // 전파가 0보다 크면 잠금이 (profagate> 0 || h == null || h.waitstatus <0)을 얻을 수 있음을 의미합니다. {// 주어진 노드의 후속 노드를 가져옵니다. // 주어진 노드의 후속 노드가 비어 있거나 해당 상태가 공유 상태 인 경우 (s == null || s.isshared ()) {// 후속 노드 DoreLeasShared ()를 깨우십시오. }}} // 릴리스 잠금 작동 (공유 모드) private void doreLeasShared () {for (;;) {// 동기 큐 노드의 헤드 노드를 가져옵니다 H = 헤드; if (h! = null && h! = tail) {// 헤드 노드의 대기 상태를 가져옵니다 int ws = h.waitstatus; // 헤드 노드의 상태가 신호 인 경우 누군가가 if (ws == node.signal) 뒤에 대기하고 있음을 의미합니다. {// 헤드 노드의 대기 상태를 0 if (! compareandsetwaitstatus (h, node.signal, 0))로 가져옵니다. } // 후속 노드 UnparkSuccessor (h)를 깨우십시오. // 헤드 노드의 상태가 0 인 경우, 나중에 큐잉하지 않으면 헤드 상태를 전파하기 위해 수정하십시오} else (ws == 0 &&! compareandsetwaitstatus (h, 0, node.propagate)) {계속; }} // 기간 동안 헤드 노드가 수정되지 않았는지 확인하면 (h == head) {break; }}}SetheadandPropagate 방법을 호출하면 먼저 헤드 노드로 설정된 다음 전달 된 TryAcquireshared 방법의 반환 값에 따라 후속 노드를 깨울지 결정합니다. 앞에서 언급했듯이 리턴 값이 0보다 큰 경우, 현재 노드가 잠금을 성공적으로 획득하고 후속 노드가 잠금을 성공적으로 획득 할 수 있음을 의미합니다. 현재 현재 노드는 공유 모드에있는 노드를 깨우야합니다. 깨어날 때마다 다음 노드만으로 깨어나는 것입니다. 후자의 노드가 공유 모드에 있지 않으면 현재 노드가 방에 직접 들어가고 추가 노드를 깨우지 않습니다. 공유 모드에서 후계자 노드를 깨우는 작업은 도리 제목 방법으로 수행됩니다. 공유 모드 및 독점 모드의 모닝 작업은 기본적으로 동일합니다. 둘 다 좌석에서 브랜드를 찾습니다 (대기 상태). 브랜드가 신호라면 누군가가 나중에 깨우는 데 도움이 필요하다는 것을 의미합니다. 브랜드가 0 인 경우 현재 대기열에서 대기열이 없음을 의미합니다. 독점 모드에서는 아무도 대기열이 없다는 것을 알게되면 대기열을 직접 남겨 둡니다. 공유 모드에서, 큐 뒤에 대기열이 없다는 것을 알게되면, 현재 노드는 나중에이 잠금 장치의 사용 가능한 상태를 나중에 사람들에게 알리기 위해 떠나기 전에 (전파 될 대기 상태를 설정 함) 여전히 작은 메모를 남깁니다. 그런 다음 나중에 오는 사람 이이 상태에 따라 잠금을 직접 취득할지 판단 할 수 있습니다.
2. 스레드 인터럽트 획득에 대한 응답
// 인터럽트 모드에서 잠금을 획득 (공유 모드) 공개 최종 void arceResharedInterpruptibly (int arg)는 중단 exception {// 먼저 스레드가 중단되었는지 여부를 결정합니다. 그렇다면 (thread.interrupted ()) {new interruptedException (); } // 1. (tryacquireshared (arg) <0) {// 2. 취득이 실패하면이 방법을 입력하십시오. }} // 인터럽트 모드에서 획득 (공유 모드) 개인 void doacquiresharedinterrupruply (int arg)는 인터럽트 exception {// 전류 노드를 동기화 큐의 꼬리에 삽입 최종 노드 노드 = Addwaiter (node.shared); 부울 실패 = 참으로; try {for (;;) {// 이전 노드 최종 노드를 가져옵니다. p = node.predecessor (); if (p == head) {int r = tryacquireshared (arg); if (r> = 0) {SetheadandPropagate (Node, R); p.next = null; 실패 = 거짓; 반품; }} if (whildparkfterfailedacquire (p, node) && parkandcheckinterrupt ()) {// 차단 프로세스 중에 스레드가 인터럽트 요청을 받으면 즉시 예외가 발생하면 새로운 인터럽트 exception (); }}} 마침내 {if (실패) {cancelAcquire (노드); }}}스레드 인터럽트에 대한 응답으로 잠금 장치를 획득하는 방법과 스레드 인터럽트에 대한 응답으로 잠금을 얻는 방법은 기본적으로 프로세스에서 동일합니다. 유일한 차이점은 스레드 인터럽트 요청에 응답 할 위치입니다. 스레드 인터럽트가 자물쇠를 얻기 위해 스레드 인터럽트에 응답하지 않으면 파크 밴드 체크 린 터그 드문 메소드에서 스레드가 깨어납니다. 일어나서 인터럽트 요청이 접수되었는지 즉시 반환합니다. 인터럽트 요청이 수신 되더라도 인터럽트 요청에 응답하고 자체적으로 매달릴 때까지 획득 될 때까지 계속 회전합니다. 스레드가 깨어 난 후 스레드는 즉시 인터럽트 요청에 응답합니다. 차단 프로세스 중에 스레드 인터럽트가 수신되면 인터럽트 픽인이 즉시 발생합니다.
3. 시간 초과 시간을 설정하십시오
// 제한된 시간 초과 (공유 모드)로 자물쇠를 획득하는 공개 최종 부울 tryacquiresharednanos (int arg, long nanostimeout)는 InterruptedException {if (thread.interrupted ()) {strach new InterruptedException (); } // 1. 자물쇠를 얻으려면 tryacquireshared에게 전화를 걸어 // 2. 인수가 실패하면 Doacquiresharednanos를 전화로 Refling tryacquireshared (arg)> = 0 || doacquiresharednanos (arg, nanostimeout);} // 제한된 시간 초과 (공유 모드) 개인 부울 부울 Doacquiresharednanos (int arg, long nanostimeout)로 잠금을 획득합니다. 중단 값 {long lasttime = system.nanoTime (); 최종 노드 노드 = addwaiter (node.shared); 부울 실패 = 참으로; try {for (;;) {// 현재 노드 최종 노드의 이전 노드를 가져옵니다. p = node.predecessor (); if (p == head) {int r = tryacquireshared (arg); if (r> = 0) {SetheadandPropagate (Node, R); p.next = null; 실패 = 거짓; 진실을 반환하십시오. }} // 타임 아웃이 소비되면 획득이 종료되고 (nanostimeout <= 0) {return false; } // 1. 스레드 서스펜션 요구 사항이 충족되는지 확인하십시오 (전방 노드 상태가 신호 임) // 2. 시간 초과 시간이 스핀 타임보다 큰지 확인하십시오. } long now = system.nanotime (); // 잠금 획득 시간을 빼면 타임 아웃 시간 nanostimeout- = 지금 - 마지막 시간; 마지막 시간 = 지금; // 차단 중에 인터럽트 요청이 수신되면 (Thread.interrupted ()) {New New InterruptedException (); }}} 마침내 {if (실패) {cancelAcquire (노드); }}}위의 두 가지 획득 방법을 이해하면 시간 초과 시간의 획득 방법을 설정하는 것이 매우 쉽습니다. 기본 프로세스는 주로 타임 아웃 메커니즘을 이해하는 것과 동일합니다. 잠금 장치가 처음으로 획득되면 Doacquiresharednanos 방법이 호출되고 시간 초과 시간이 전달됩니다. 방법을 입력하면 상황에 따라 잠금이 다시 획득됩니다. 잠금이 다시 실패하면 스레드가 매달린 것으로 간주되어야합니다. 현재 시간 초과 시간이 스핀 타임보다 큰지 여부를 결정합니다. 그렇다면 스레드는 일정 시간 동안 일시 중단됩니다. 그렇지 않으면, 우리는 그것을 계속 얻으려고 노력할 것입니다. 잠금 장치를 얻을 때마다 잠금 시간을 획득 할 시간을 빼냅니다. 타임 아웃 시간이 소진 될 때까지 이와 같이 반복됩니다. 잠금 장치가 취득되지 않으면 취득이 종료되고 취득 실패 플래그가 반환됩니다. 스레드는 기간 동안 스레드 인터럽트에 응답합니다.
4. 공유 모드에서 노드의 작업을 삭제합니다
// 잠금 잠금 해제 (공유 모드) 공개 최종 부울 방출 (int arg) {//1. if (tryReleasShared (arg)) {// 2. 릴리스가 성공하면 다른 스레드 DoreLeasShared ()를 깨우십시오. 진실을 반환하십시오. } return false;} // 잠금 장치 (공유 모드) 보호 된 부울 tryreleaseShared (int arg) {throw new unsupportedoperation exception ();} // 릴리스 잠금 작동 (공유 모드) 개인 void doreLeasShared () {for (;;) {// 헤드 노드 h = 헤드; if (h! = null && h! = tail) {// 헤드 노드의 대기 상태를 가져옵니다 int ws = h.waitstatus; // 헤드 노드의 상태가 신호 인 경우, 나중에 누군가 (ws == node.signal) {// 먼저 헤드 노드의 대기 상태를 0 if (! compareandsetwaitstatus (h, node.signal, 0))로 업데이트한다는 것을 의미합니다. } // 후속 노드를 깨우지 못합니다. // 헤드 노드의 상태가 0이면 나중에 대기하지 않으면 헤드 상태를 전파로 변경} else (ws == 0 &&! CompareAndSetwaitStatus (h, 0, node.propagate)) {계속; }} // 루프는 (h == head) {break; }}}스레드가 방에서 작업을 마치면 잠금 장치를 해제하기 위해 방출 방법을 호출합니다. 먼저, tryreleaseShared 방법을 호출하여 잠금을 해제하려고합니다. 이 방법의 판단 논리는 서브 클래스에 의해 구현됩니다. 릴리스가 성공하면 DoreLeasShared 방법에 전화하여 후속 노드를 깨우십시오. 방 밖으로 나간 후에는 원래 좌석 (헤드 노드)을 찾아서 누군가가 좌석에 작은 메모를 남겼는지 확인합니다 (상태 신호). 그렇다면 후속 노드를 깨우십시오. (상태 0)가 없다면 대기열에서 대기열이 없다는 것을 의미하는 경우, 떠나기 전에 마지막으로해야 할 일은 떠나는 것입니다. 즉, 자물쇠 뒤에있는 사람들에게 상태를 취득하도록 지시하기 위해 좌석에 작은 메모를 남겨 두는 것입니다. 전체 잠금 해제 프로세스와 독점 모드의 유일한 차이점은이 마지막 단계에서 작동하는 것입니다.
참고 : 위의 모든 분석은 JDK1.7을 기반으로하며 다른 버전간에 차이가있을 것이므로 독자는주의를 기울여야합니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.