이전 기사 "Java Concurrency Series [1] ----- AbstractQueuedSynchronizer 소스 코드 분석"에서, 우리는 AbstractQueuedSynchronizer의 몇 가지 기본 개념, 주로 AQS의 대기열 영역, 독점 모드 및 공유 모드 및 대기 상태의 상태를 이해하는 방법에 대해 이야기했습니다. 이러한 내용을 이해하고 마스터하는 것은 AQS 소스 코드를 후속 읽는 핵심이므로 독자는 먼저 이전 기사를 읽은 다음이 기사를 되돌아 보는 것이 좋습니다. 이 기사에서는 노드가 독점 모드에서 동기화 큐 큐를 입력하는 방법과 동기화 큐를 떠나기 전에 어떤 작업을 수행 할 것인지 소개합니다. AQS는 독점 모드 및 공유 모드에서 잠금을 얻는 세 가지 방법을 제공합니다. 비 응답 스레드 인터럽트 획득, 응답 스레드 인터럽트 획득 및 시간 초과 획득을 설정합니다. 이 세 가지 방법의 전체 단계는 몇 가지 다른 부분만으로 거의 동일하므로 하나의 방법을 이해하고 다른 방법의 구현을 보면 유사합니다. 이 기사에서는 스레드 인터럽트에 응답하지 않는 획득 방법에 중점을 둘 것이며 다른 두 가지 방법은 불일치에 대해서도 이야기 할 것입니다.
1. 응답이없는 스레드 인터럽트로 자물쇠를 얻는 방법은 무엇입니까?
// 인터럽트 메소드 획득 (독점 모드) 공개 최종 무효 획득 (int arg) {if (! tryacquire (arg) && acquirequeued (addwaiter (node.exclusive), arg)) {secrinterrupt (); }}위의 코드는 간단 해 보이지만 아래 그림에 표시된 4 단계를 순서대로 수행합니다. 아래에서 우리는 단계별로 시연하고 분석합니다.
1 단계 :! tryacquire (arg)
// 잠금 장치 (독점 모드) Protected Boolean TryAcquire (int arg) {새 새로운 UnsupportedOperationException ()을 던지십시오.현재 누군가가 와서 먼저 문을 두드리려고했습니다. 만약 그가 문이 잠겨 있지 않다는 것을 알게된다면 (tryacquire (arg) = true). 도어가 잠겨 있다는 것을 알게되면 (tryacquire (arg) = false) 다음 단계를 수행하십시오. 이 tryacquire 방법은 잠금이 열린시기와 잠금이 닫힌시기를 결정합니다. 이 방법은 서브 클래스로 덮어 쓰고 내부 판단 논리를 다시 작성해야합니다.
2 단계 : Addwaiter (node.exclusive)
// 현재 스레드를 노드로 랩핑하고 동기화 큐의 꼬리에 추가 개인 노드 AddWaiter (노드 모드) {// 잠금 노드를 고정하는 모드를 지정합니다. // 동기화 큐 노드의 테일 노드 참조를 가져옵니다. pred = 꼬리; // 테일 노드가 비어 있지 않으면 동기화 큐에 이미 (pred! = null) {// 1. 현재 꼬리 노드 노드를 가리 킵니다 .prev = pred; // 2. 전류 노드를 꼬리 노드로 설정 if (compareAndSettail (pred, node)) {// 3. 오래된 꼬리 노드의 후계자를 새로운 테일 노드 pred.next = 노드로 가리 킵니다. 리턴 노드; }} // 그렇지 않으면 동기화 큐가 초기화되지 않았 음을 의미합니다. return node;} // node enqueue private node enq (final node node) {for (;;) {// 동기화 큐 노드 t = 꼬리의 테일 노드 참조를 가져옵니다. // 테일 노드가 비어 있으면 (t == null) {// 동기화 큐를 초기화하면 동기화 큐가 초기화되지 않았 음을 의미합니다. if (compareAndSethead (node ()) {tail = head; }} else {// 1. 현재 꼬리 노드 노드를 가리 킵니다 .prev = t; // 2. 전류 노드를 꼬리 노드로 설정 if (compareAndSettail (t, node)) {// 3. 오래된 꼬리 노드의 후속 인을 새로운 테일 노드 T.next = 노드로 가리 킵니다. 반환 t; }}}}이 단계에 대한 실행은 잠금 획득이 처음 실패했을 때 자신을 위해 숫자 카드를 얻고 대기열 영역에 큐를 입력 할 것임을 나타냅니다. 숫자 카드를받을 때, 그는 방을 점유하고 싶은 방법 (독점 모드 또는 공유 모드)을 선언합니다. 그는 지금 앉아서 쉬지 않았다는 점에 유의하십시오 (매달리십시오).
3 단계 : AcquireQueued (addwaiter (node.exclusive), arg)
// 무정형 방식으로 잠금을 획득 (독점 모드) 최종 부울 획득 (최종 노드 노드, int arg) {부울 실패 = true; {boolean Interrupted = false; for (;;) {// 주어진 노드 최종 노드의 이전 노드 참조를 가져옵니다. p = node.predecessor (); // 현재 노드가 동기화 큐의 첫 번째 노드 인 경우 (p == head && tryacquire (arg)) {// 주어진 노드를 헤드 노드 sethead (노드)로 설정하십시오. // 쓰레기 수집을 돕기 위해 이전 헤드 노드의 후속 인 P.NEXT = NULL; // 성공적인 획득 상태를 설정 실패 = false; // 인터럽트 상태를 반환하고 전체 루프가 여기에서 실행되고 출구 반환이 중단되었습니다. } // 그렇지 않으면 잠금 상태를 여전히 사용할 수 없음을 의미합니다. 이 시점에서 현재 스레드가 현탁 될 수 있는지 확인하십시오. // 결과가 참이면, 현재 스레드가 일시 중단 될 수 있습니다. 그렇지 않으면이 기간 동안 루프가 계속됩니다.이 기간 동안 스레드는 (DUTPARKAFTERFAILEDACQUIRE (p, node) && parkAndCheckInterrupt ()) {interrupted = true; }}} 마지막으로 {// 획득을 취소하십시오 (실패) {cancelAcquire (node); }}} // 현재 노드를 중단시킬 수 있는지 판단하십시오. 개인 정적 부울 STATIC BOOLEAN DOTHKARFTERFAILEDACQUIRE (Node Pred, Node Node) {// 전방 노드의 대기 상태를 가져옵니다 int ws = pred.waitstatus; // 전방 노드 상태가 신호 인 경우, 전방 노드가 현재 노드를 깨우므로 현재 노드가 IF (ws == node.signal) {return true; } if (ws> 0) {// 다음 작업은 동기화 큐에서 취소 된 모든 포워드 노드를 정리하는 것입니다. } while (pred.waitstatus> 0); pred.next = 노드; } else {//이 끝까지, 이는 전방 노드의 상태가 신호가 아니며 0과 같을 가능성이 높습니다. 이러한 방식으로 전방 노드는 현재 노드를 깨우지 않을 것입니다 .// 현재 노드의 상태가 SAPINDSETWAITSTATUS (PREST, WS, NODE.Signal)를 안전하게 교수형으로 여야합니다. } return false;} // 현재 스레드 중단 비공개 최종 ParkAndCheckinterRUPT () {locksupport.park (this); return shread.interrupted ();}숫자 부호를 얻은 후에는이 방법을 즉시 구현합니다. 노드가 처음으로 큐 영역에 들어가면 두 가지 상황이 있습니다. 하나는 그 앞에있는 사람이 자리를 떠나 방에 들어갔다는 것을 알게되므로 앉아서 쉬지 않고 다시 문을 두드려 아이가 끝났는지 확인할 것입니다. 내부의 사람이 방금 끝났다면, 그는 자신을 부르지 않고 서두르게 될 것입니다. 그렇지 않으면, 그는 앉아서 잠시 쉬고있는 것을 고려해야했지만 여전히 걱정했다. 그가 앉아서 잠들었을 때 아무도 그에게 상기시켜주지 않았다면 어떨까요? 그는 내부에서 나온 사람이 메모를 본 후 그를 깨울 수 있도록 남자의 자리에 작은 메모를 남겼습니다. 또 다른 상황은 그가 큐 영역에 들어 와서 몇 명의 사람들이 그 앞에 대기열이 있다는 것을 알았을 때 잠시 동안 앉을 수 있었지만 그 전에는 그 자리에 자리에 메모를 남겨두고 (이 시간에 이미 잠들었습니다), 그 사람이 떠나기 전에 그를 깨울 수있었습니다. 모든 것이 끝나면 그는 평화롭게 자고 있습니다. 루프의 전체가 하나의 출구 만 가지고 있음을 알 수 있습니다. 즉, 스레드가 잠금을 성공적으로 획득 한 후에 만 나갈 수 있습니다. 잠금 장치가 얻기 전에 For Loop의 Parkandcheckinterrupt () 메소드에 항상 매달려 있습니다. 실이 깨어 난 후에도이 장소에서 For 루프를 계속 실행합니다.
4 단계 : Selfinterrupt ()
// 현재 스레드는 자체를 방해합니다. 비공개 정적 void selfinterrupt () {thread.currentThread (). interprupt (); }위의 전체 스레드가 For Loop의 ParkandCheckinterRup () 메소드에 매달려 있으므로 성공적으로 획득하기 전에 모든 형태의 스레드 인터럽트에 응답하지 않습니다. 스레드가 잠금을 성공적으로 얻고 For Loop에서 나오면 누군가 가이 기간 동안 스레드를 방해하라고 요청하는지 확인합니다. 그렇다면 selfinterrupt () 메소드를 호출하여 자체적으로 걸으십시오.
2. 스레드 인터럽트에 대한 응답으로 잠금을 얻는 방법은 무엇입니까?
// 인터럽트 모드 (독점 모드)에서 잠금을 획득하는 개인 void doacquireinterpruptibly (int arg)는 인터럽트 exception {// 현재 스레드를 노드로 래핑하고 동기화 큐 최종 노드 노드 = AddWaiter (node.exclusive)에 추가합니다. 부울 실패 = 참으로; try {for (;;) {// 이전 노드 최종 노드 P = Node.predecessor (); // p가 헤드 노드 인 경우, 현재 스레드는 (p == head && tryacquire (arg)) {sethead (node); p.next = null; // gc 실패 = false; // 잠금이 성공적으로 획득 된 후 리턴 리턴; } // 조건이 충족되면 현재 스레드가 일시 중단됩니다. 이 시점에서 인터럽트가 응답되고 (thuTparkAfterfailedAcquire (p, node) && parkandcheckinterrupt ()) {// 스레드가 깨어나면 인터럽트 요청이 발견되면 예외가 발생하면 예외가 발생합니다. 새로운 InterruptedException ()을 던지십시오. }}} 마침내 {if (실패) {cancelAcquire (노드); }}}응답 스레드 인터럽트 방법 및 비 응답 스레드 인터럽트 방법은 잠금을 얻는 과정에서 거의 동일합니다. 유일한 차이점은 스레드가 Parkandcheckinterrupry 말하기에서 깨어 난 후에는 스레드가 중단되었는지 확인한다는 것입니다. 그렇다면 InterruptedException 예외가 발생합니다. 스레드 인터럽트 획득 잠금 장치에 응답하는 대신 인터럽트 요청을 수신 한 후에 만 인터럽트 상태를 설정하고 잠금 획득의 현재 방법을 즉시 종료하지 않습니다. 노드가 잠금을 성공적으로 획득 한 후 인터럽트 상태를 기준으로 자체적으로 매달릴지 여부를 결정하지 않습니다.
3. 잠금을 얻기 위해 타임 아웃 시간을 설정하는 방법은 무엇입니까?
// 제한된 시간 초과 (독점 모드)로 잠금을 획득하는 개인 부울 doacquirenanos (int arg, long nanostimeout)는 중단 exception {// 현재 시스템 시간을 길게 얻는 마지막 시간 = system.nanotime (); // 현재 스레드를 노드로 래핑하고 동기화 큐에 추가합니다. 최종 노드 노드 = AddWaiter (node.exclusive); 부울 실패 = 참으로; try {for (;;) {// 이전 노드 최종 노드 P = Node.predecessor (); // 이전 노드가 헤드 노드 인 경우 현재 스레드는 (p == head && tryacquire (arg)) {// 헤드 노드 sethead (노드)를 업데이트합니다. p.next = null; 실패 = 거짓; 진실을 반환하십시오. } // 타임 아웃 시간이 다가 오면 (nanostimeout <= 0) {return false; } // 타임 아웃 시간이 스핀 타임보다 큰 경우 스레드가 매달릴 수 있다고 판단 한 후에는 스레드가 일정 시간 동안 일시적으로 일시 중지됩니다 (DUTHKARKTERFAILEDACQUIRE (p, node) && nanostimeout> spinfortimeoutthreshold) {// 현재 스레드를 잠금하고 자체적으로 깨어납니다. } // 시스템의 현재 시간을 길게 가져옵니다. now = system.nanoTime (); // 타임 아웃 시간은 획득 잠금 잠금 장치의 시간 간격에서 빼면 Nanostimeout- = 지금 - 마지막 시간; // 마지막으로 다시 업데이트 마지막 시간 = 지금; // 잠금 if (thread.interrupted ()) {wash new InterruptedException (); }}} 마침내 {if (실패) {cancelAcquire (노드); }}}시간 초과 시간 획득을 설정하면 먼저 잠금 장치가 획득됩니다. 처음 획득이 실패하면 상황에 근거합니다. 들어오는 시간 초과 시간이 스핀 타임보다 크면 스레드가 일정 시간 동안 매달리면 회전됩니다. 잠금이 취득 될 때마다, 잠금을 얻는 데 걸리는 시간으로부터 시간 초과 시간을 빼게됩니다. 시간 초과가 0보다 작을 때까지 시간 초과 시간이 소비되었음을 의미합니다. 잠금을 취득하는 작업이 종료되고 획득 실패 플래그가 반환됩니다. 시간 초과 시간에 따라 잠금을 획득하는 과정에서 스레드 인터럽트 요청에 응답 할 수 있습니다.
4. 스레드는 어떻게 잠금을 방출하고 동기화 대기열을 떠나는가?
// 릴리스 잠금 작동 (독점 모드) 공개 최종 부울 릴리스 (int arg) {// 비밀번호 잠금을 돌려서 잠금을 해제 할 수 있는지 확인하십시오. // 헤드 노드가 비어 있지 않고 대기 상태가 0과 같지 않으면 (h! = null && h.waitstatus! = 0) if if if if if If If If If If If If If If If If inc } true를 반환합니다. } return false;} // 후속 Node 개인 void unparksuccessor (노드 노드) {// 주어진 노드 int ws = node.waitstatus의 대기 상태를 가져옵니다. // 대기 상태를 0 if (ws <0)로 업데이트합니다. } // 주어진 노드 노드의 후속 노드를 가져옵니다 s = node.next; // 후속 노드가 비어 있거나 대기 상태가 취소됩니다. // (Node t = tail; t! = null && t! = 노드; t = t.prev) {if (t.waitstatus <= 0) {s = t; }}} // 주어진 노드 후 첫 번째 노드를 깨우십시오. }}스레드가 잠금 장치를 실내로 잡으면 자체 사업을 수행합니다. 작업이 완료되면 자물쇠가 풀리고 방을 떠납니다. TryRelease 방법을 통해 암호 잠금을 잠금 해제 할 수 있습니다. 우리는 tryrelease 방법을 서브 클래스에 의해 덮어 써야한다는 것을 알고 있습니다. 다른 서브 클래스의 구현 규칙은 다릅니다. 즉, 다른 서브 클래스로 설정된 비밀번호가 다릅니다. 예를 들어, 재 렌트 링크에서, 방의 사람이 tryrelease 방법을 호출 할 때마다 상태가 0으로 줄어들 때까지 상태는 1만큼 줄어 듭니다. 비밀번호 잠금이 열립니다. 이 프로세스가 우리가 암호 잠금 장치의 휠을 지속적으로 돌리는 것처럼 보이고, 회전 할 때마다 바퀴 수를 1 씩 줄입니다. CountdownLatch는 한 사람을 돌리는 것이 아니라 한 사람을 돌려서 잠금을 열기 위해 모든 사람의 힘을 집중시킬 것이라는 점을 제외하고는이 것과 약간 비슷합니다. 실이 방을 떠난 후에는 원래 좌석을 찾을 수 있습니다. 즉, 헤드 노드를 찾습니다. 누군가가 좌석에 작은 메모를 남겼는지 확인하십시오. 있다면, 누군가가 잠들고 그것을 깨우도록 요청해야한다는 것을 알게 될 것입니다. 그러면 그 실이 깨어날 것입니다. 그렇지 않다면, 그것은 당분간 동기화 대기열에서 기다리고 있지 않으며 아무도 깨어날 필요가 없으므로 마음의 평화로 떠날 수 있음을 의미합니다. 위의 프로세스는 독점 모드에서 잠금을 공개하는 프로세스입니다.
참고 : 위의 모든 분석은 JDK1.7을 기반으로하며 다른 버전간에 차이가있을 것이므로 독자는주의를 기울여야합니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.