マインドマップギャラリー Java 동시성 잠금 도구
이것은 Java 동시성 잠금 도구에 대한 마인드 맵입니다. 이러한 동시성 잠금 도구는 Java의 동시성 프로그래밍에서 중요한 역할을 할 수 있으며 효율적이고 안전한 동시성 코드를 작성하는 데 도움이 됩니다.
2024-01-18 10:28:15 に編集されましたJava 동시성 잠금 도구
AbstractQueueSynchronizer
소개
주로 동기화 상태를 관리하는 데 사용되는 잠금 장치 또는 기타 동기화 구성 요소를 구축하기 위한 기본 프레임워크입니다.
주요 용도는 상속입니다.
차단은 한 순간에만 발생할 수 있으므로 컨텍스트 전환 비용이 줄어듭니다.
상속하다
AbstractOwnableSynchronizer
스레드가 배타적 모드를 점유할 수 있도록 하는 동기화 장치의 기본 구현
멤버변수
비공개 임시 스레드 ExclusiveOwnerThread
현재 동기화 상태를 차지하고 있는 스레드
주요 방법
보호된 최종 무효 setExclusiveOwnerThread(스레드 스레드)
전용 스레드 설정
보호된 최종 스레드 getExclusiveOwnerThread()
현재 독점 스레드 가져오기
인터페이스 구현
직렬화 가능
내부 클래스
정적 최종 클래스 노드
소개
AQS는 내부적으로 양방향 FIFO 동기화 대기열을 사용하여 동기화 상태 관리를 완료합니다. 스레드가 획득에 실패하면 대기열의 구조가 노드에 추가됩니다. AQS에서. 조건 큐도 Node의 정의를 사용하지만 다른 큐에 있습니다.
멤버변수
정적 상수
정적 최종 노드 SHARED = 새 노드()
노드가 공유 모드에 있음을 나타냅니다.
정적 최종 노드 EXCLUSIVE = null
노드가 단독 모드에 있고 단 하나의 스레드만 동시에 동기화 상태를 유지함을 나타냅니다.
정적 최종 int 취소됨 = 1
노드(스레드)는 시간 초과 또는 중단으로 인해 이 상태에 있으며 이후에 다른 상태로 변경되지 않으며 더 이상 차단되어서도 안 됩니다.
정적 최종 int 신호 = -1
노드(스레드)가 이 상태일 때, 후속 노드는 차단된 상태(park를 통해)이거나 그럴 예정이므로 현재 노드가 잠금을 해제하거나 취소되면 unpark를 호출하여 후속 노드를 깨워야 합니다. .
정적 최종 int 조건 = -2
현재 노드(스레드)가 조건 큐(await를 통해)에 있고 특정 순간(신호를 통해) 상태가 0으로 설정될 때까지 동기화 큐의 일부가 아님을 나타냅니다.
정적 최종 int PROPAGATE = -3
공유 잠금 전파(헤드 노드에서 사용)
휘발성 int waitStatus
위의 CANCELLED/SIGNAL/CONDITION/PROPAGATE 및 0(이 네 가지 상태가 아님을 나타냄)을 포함하여 음수가 아닌 숫자는 노드에 상호 작용이 필요하지 않음을 나타냅니다.
휘발성 노드
선행 노드
휘발성 노드 다음
후속 노드
휘발성 스레드 스레드
노드에 해당하는 스레드
노드 nextWaiter
동기화 큐에서는 노드가 공유 상태인지 배타적 상태인지를 나타내며 SHARED는 공유를 나타내고 null은 조건부 큐에서 배타성을 나타내며 다음 노드에 대한 참조를 나타냅니다.
조건은 동기화 큐가 배타적 모드일 때만 사용할 수 있으므로 이 변수는 공유되도록 설계되었습니다.
주요 방법
공법
마디()
초기 노드를 설정하거나 SHARED 노드를 구성하는 데 사용됩니다.
노드(스레드 쓰레드, 노드 모드)
addWaiter에서 사용
Node(스레드 스레드, int waitStatus)
상태로 사용됨
회원 방법
최종 부울 isShared()
nextWaiter 반환 == 공유됨
최종 노드 전임자()
선행 노드를 가져오고, null인 경우 예외를 발생시킵니다.
공개 클래스 ConditionObject
소개
Condition 인터페이스의 구현은 기본 Lock 구현을 제공합니다. 노드를 사용하여 대기 대기열을 구성합니다. 각 조건 객체에는 FIFO 대기열(단방향)이 포함됩니다.
큐는 노드의 nextWaiter 속성을 다음 노드에 대한 참조로 사용합니다.
인터페이스 구현
상태
직렬화 가능
멤버변수
정적 상수
개인 정적 최종 int REINTERRUPT = 1
대기를 종료할 때 다시 인터럽트해야 함을 나타냅니다.
개인 정적 최종 int THROW_IE = -1
대기를 종료할 때 InterruptedException이 발생해야 함을 나타냅니다.
비공개 임시 노드 firstWaiter
조건부 큐의 헤드 노드를 가리킵니다.
개인 임시 노드 lastWaiter
조건 대기열의 꼬리 노드를 가리킵니다.
주요 방법
공개 최종 무효가 기다리고 있습니다()
호출 스레드를 대기 대기열에 진입시키고 잠금을 해제한 후 차단 상태로 들어갑니다. signal/signalAll에 의해 노드가 깨어난 후 차단이 인터럽트에 의해 깨어나면 인터럽트에 응답합니다.
개인 노드 addConditionWaiter()
대기 대기열에 새 노드를 추가합니다.
특정 구현 논리: lastWaiter가 취소된 것으로 확인되면 unlinkCancelledWaiters를 호출하여 전체 대기열에서 취소된 메서드를 지운 다음(firstWaiter 및 lastWaiter 모두 새 노드를 가리킬 수 있음) 새 노드(CONDITION 상태)를 만들고 이를 대기열의 끝
개인 무효 연결 해제CancelledWaiters()
firstWaiter부터 waitStatus가 CONDITION이 아닌 노드를 지우려면 while을 사용합니다. 메서드 호출은 잠금이 해제되기 전이므로 루프 프로세스를 잠글 필요가 없습니다. GC의 잔여물을 피하기 위해 신호가 없는 경우 타임아웃이나 취소가 발생한 경우에만 이 방법을 사용합니다.
조건 대기열에 있는 노드의 waitStatus에는 CONDITION 및 CANCELLED라는 두 가지 상태만 있어야 합니다.
final int FullRelease(노드 노드)
잠금 상태를 해제합니다. 잠금 상태가 해제되지 않으면 IllegalMonitorStateException이 발생하고 노드는 CANCELED 상태로 설정됩니다. 성공하면 해제 전 상태 값이 반환됩니다.
풀어 주다
여기서는 AQS의 해제 방법을 호출하고, 깨어나는데 적합한 노드를 찾습니다.
매개변수는 AQS의 상태 속성을 사용합니다.
최종 부울 isOnSyncQueue(노드 노드)
노드가 동기화 큐에 있는지 확인합니다(조건 큐가 아니라 동기화 큐임에 유의하세요). waitStatus가 CONDITION인 경우 노드의 이전 또는 다음이 동기화 큐에 없다는 의미입니다. null인 경우 이는 동기화 대기열에 없음을 의미하기도 합니다(이 두 속성은 동기화 대기열에 사용됩니다. 조건부 대기열의 선행자와 후속자는 이 두 속성을 사용하지 않습니다). 그런 다음 findNodeFromTail 메서드를 호출하여 동기화를 순회합니다. 노드가 그 안에 있는지 확인하기 위해 테일 노드의 큐에 넣습니다.
동기화 대기열과 조건 대기열의 관계: 잠금 경쟁은 동기화 대기열에만 의존합니다. 조건 대기열은 조건 부족으로 인해 차단된 스레드만 저장합니다. 스레드가 잠금을 위해 경쟁해야 하는 경우에도 동기화 대기열로 변환해야 합니다.
이 방법은 코드에서 while의 판단 조건으로 사용됩니다. 노드가 동기화 대기열에 없으면(signal/signalAll이 아님을 나타냄) while에 들어가 Park를 통해 차단됩니다. 노드가 깨어나면 먼저 인터럽트에 의해 깨어났는지 확인하고, 그렇지 않으면 계속해서 while 프로세스로 돌아가고, 그렇지 않으면 해당 노드를 동기화 대기열에 추가하려고 시도합니다. 프로세스.
acquireQueued(노드, 저장된 상태)
동기화 대기열에 있는 노드 노드에 대해 계속해서 selectedState 동기화 상태를 얻으려고 시도합니다.
연결 해제취소됨웨이터
잠금을 획득한 후 node.nextWaiter가 null이 아닌 경우 이 메서드를 호출하여 취소된 상태의 노드를 지웁니다.
보고인터럽트후대기
이전에 인터럽트로 인해 깨어났다면, 인터럽트 상태를 재설정할지 아니면 인터럽트 예외를 발생시킬지 이전 판단 결과에 따라 인터럽트를 처리해야 합니다.
공개 최종 긴 waitNanos(long nanosTimeout)
기본 논리는 대기 시간과 유사하며 시간 초과 판단이 추가됩니다.
transferAfterCancelledWait 후반 부분이 잘 이해가 되지 않습니다.
public final boolean wait(오랜 시간, TimeUnit 단위)
기본 로직은 waitNanos와 유사하며 시간 단위를 지정할 수 있으며 최종적으로 계산을 위해 나노초로 변환됩니다.
공개 최종 무효 waitUninterruptously()
기본 논리는 대기와 유사하며 인터럽트에 응답하지 않습니다(즉, selfInterrupt가 인터럽트 상태를 재설정함).
공개 최종 부울 waitUntil(날짜 마감일)
기본 논리는 대기와 유사하며 최대 차단 시간이 추가됩니다.
공개 최종 무효 신호()
조건부 큐에서 가장 오래 대기하는 스레드를 동기화 큐로 이동
보호된 부울 isHeldExclusively()
현재 스레드가 독점 모드에서 잠금을 보유하고 있는지 여부를 반환합니다. 그렇다면 true를 반환합니다. 이 메소드는 AQS에 있으며 구체적인 구현을 제공하지 않습니다. 그러나 이 메소드는 조건에서만 사용되므로 ConditionObject를 사용하지 않는 경우에는 구현할 필요가 없으며, 그렇지 않으면 구현해야 합니다.
private void doSignal(노드 우선)
조건 큐에서 노드를 제거하고 이를 동기화 큐의 노드로 변환합니다. 구현 프로세스는 null이 아니고 CANCELED 상태가 아닌 노드를 만날 때까지 앞에서 뒤로 루프로 이루어지며, 이를 변환하고 루프를 종료합니다.
최종 부울 전송ForSignal(노드 노드)
조건 큐에서 동기화 큐로 노드를 변환하고 변환 성공 여부를 반환합니다. 구현 논리는 먼저 노드 상태가 CONDITION인지 확인하고 취소된 노드가 아닌 경우 false를 반환한 다음 enq를 호출하여 동기화 대기열에 들어가고 그 후 대기열에 있는 노드의 이전 노드를 가져옵니다. 선행 노드의 상태가 0보다 크거나 CAS가 수정되었습니다. waitStatus가 실패하면(변경 사항 발생) 스레드를 언파크하고 마지막으로 true를 반환합니다.
동기화 대기열에 합류한 후 반드시 바로 깨어나는 것은 아닙니다. waitStatus>0이거나 CAS 실행 중에 변경된 경우에만 스레드가 해제되고 깨어납니다. 이때 깨어나지 않으면 동기화 큐 로직이 나올 때까지 기다렸다가 깨어나게 됩니다.
엔큐
공개 최종 무효 signalAll()
조건 대기열의 적격한(취소되지 않은) 모든 노드를 동기화 대기열로 전송합니다. 기본 논리는 signal과 유사하지만 transferForSignal 메서드가 루프에서 실행된다는 차이점이 있습니다.
독점적으로 보유됨
private void doSignalAll(노드 우선)
transferForSignal
보호된 최종 부울 hasWaiters()
조건 큐에 스레드가 있는지 여부에 관계없이 조건 큐를 처음부터 순환시키는 방식으로 상태가 CONDITION인 노드가 있으면 true를 반환하고 그렇지 않으면 false를 반환합니다.
멤버변수
정적 변수
정적 최종 긴 spinForTimeoutThreshold = 1000L;
타임아웃에 사용되는 임계값입니다. 이 값보다 작으면 타임아웃으로 파크를 호출할 필요가 없지만 이때 루프를 계속 실행하게 하면 회전 효율이 높아집니다.
개인 정적 최종 안전하지 않음 안전하지 않음
Unsafe.getUnsafe()에서는 objectFieldOffset 메소드를 통해 오프셋을 구하는데 사용되므로 이후의 수정은 Unsafe의 CAS 연산을 통해 직접 이루어질 수 있다.
개인 정적 최종 긴 상태 오프셋
AQS 상태
개인 정적 최종 긴 headOffset
AQS 헤드
개인 정적 최종 긴 tailOffset
AQS 꼬리
개인 정적 최종 긴 waitStatusOffset
노드의 waitStatus
개인 정적 최종 긴 nextOffset
다음 노드
개인 임시 휘발성 노드 헤드
동기화 대기열의 헤드 노드, 지연 로드는 헤드 노드가 존재할 때 setHead를 통해서만 수정되며 해당 상태는 CANCELLED일 수 없습니다.
비공개 임시 휘발성 노드 테일
동기화 대기열의 꼬리 노드, 지연 로딩
개인 휘발성 int 상태
동기화 상태
주요 방법
보호된 최종 int getState()
동기화 상태의 현재 값을 반환합니다.
보호된 최종 무효 setState(int newState)
현재 동기화 상태 설정
protected final boolean CompareAndSetState(int Expect, int update)
CAS는 상태를 설정하고 맨 아래 레이어는 Unsafe의 CAS 메서드를 호출합니다.
개인 노드 addWaiter(노드 모드)
대기열에 넣는 방법
지정된 모드를 사용하여 현재 스레드를 대기열 끝에 추가합니다.
주목할 가치가 있는 것은
Node 매개변수는 모드를 지정하는 데 사용되며 스레드는 currentThread를 통해 획득됩니다.
구현에서 tail이 비어 있지 않으면 신속하게 cas를 시도하여 tail을 설정하고, 성공하면 enq 메소드가 직접 반환됩니다.
프라이빗 노드 enq(최종 노드 노드)
루프(스핀) CAS. tail이 비어 있으면 CAS가 head(새 노드)를 먼저 초기화합니다. tail이 비어 있지 않으면 CAS가 tail을 설정합니다.
여기서 파라미터 노드는 모드가 아닌 추가가 필요한 노드입니다.
공개 최종 부울 hasQueuedPredecessors()
현재 스레드 앞에 노드가 있는지 확인하고, 있으면 true를 반환하고, 그렇지 않으면 false를 반환합니다. 공정한 잠금 구현에 사용됩니다. 구체적인 구현: 큐가 비어 있지 않은 경우, head의 후속 노드가 비어 있거나 현재 스레드가 아니라고 판단되면 true를 반환합니다.
head.next는 언제 null인가요?
공개 최종 부울 hasQueuedThreads()
return head != tail; 대기열에 대기 중인 스레드가 있는지 여부
공개 최종 부울 isQueued(스레드 스레드)
스레드가 대기열에 있는지 여부에 관계없이 구현은 끝에서 처음까지 반복됩니다.
공개 최종 int getQueueLength()
스레드가 null이 아닌 노드 수를 계산하기 위해 끝에서 시작까지 반복하여 구현되는 큐 길이를 가져옵니다.
공개 최종 컬렉션<Thread> getQueuedThreads()
큐에 있는 스레드의 컬렉션을 가져와 Collection 형식으로 반환합니다. 구현 방법은 ArrayList를 구성하고, 끝에서 처음까지 루프를 반복하여 null이 아닌 스레드를 추가한 다음 ArrayList 개체를 반환하는 것입니다.
공개 최종 부울 소유(ConditionObject 조건)
조건 개체가 현재 동기화 장치 AQS에 속하는지 여부
독점 모드
동시에 하나의 스레드만 동기화 상태를 얻을 수 있습니다.
공개 최종 무효 획득(int arg)
독점 모드는 동기화 상태를 획득하고 중단을 무시합니다. 먼저 tryAcquire를 호출하여 독점 잠금을 획득하려고 시도하고, 실패한 후 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)를 호출하여 동기화 대기열에 스레드를 추가하려고 시도합니다. 노드 상태, 그리고 반환된 결과에 따라 (스레드 인터럽트가 있는지 여부) 인터럽트 상태를 복원하기 위해 스레드 인터럽트 메서드를 호출합니다.
획득 자체는 인터럽트에 응답하지 않으므로 인터럽트 처리는 인터럽트 상태를 복원하는 것입니다.
보호된 부울 tryAcquire(int arg)
배타적 잠금을 얻으려고 시도하고, 성공하면 true를 반환하고, 실패하면 false를 반환합니다. 특정 구현은 하위 클래스에 의해 완료되며 동기화 상태를 얻을 때 스레드 안전성이 보장되어야 합니다.
final boolean acquireQueued(최종 노드 노드, int arg)
arg는 경쟁이 필요한 리소스로 이해될 수 있습니다. AQS는 내부적으로 상태로 표시되지만 실제로 구체적인 용도는 개발자가 정의합니다.
이미 대기열에 있는 스레드에 대한 잠금 획득을 지속적으로 시도합니다(addWaiter에 의해 노드가 생성됨).
구현 논리: 다음과 같이 회전합니다. 이전 노드가 헤드이고 잠금을 성공적으로 획득한 경우 현재 노드를 헤드로 설정하고 반환이 중단되지 않습니다. 그렇지 않으면 다음 작업을 진행합니다. 노드 상태를 확인 및 설정하려면 shouldParkAfterFailedAcquire를 호출하고 스레드를 차단하고 인터럽트 상태를 확인하려면 parkAndCheckInterrupt를 호출하세요. 프로세스 중에 예외가 발생하면 cancelAcquire를 호출하여 획득을 취소합니다.
실제로 잠금을 획득하지 못하더라도 루프는 멈추지 않습니다. 이전 노드의 상태가 성공적으로 설정되고 스레드가 파크로 중단되면 스레드가 일시 중단됩니다. 헤드 뒤의 노드는 tryAcquire를 사용하여 잠금에 대한 새 스레드와 경쟁합니다. 이는 여기서 잠금 구현이 불공평함을 나타냅니다.
개인 무효 setHead(노드 노드)
노드를 헤드로 설정하고 스레드 및 이전 속성을 null로 설정하여 GC를 용이하게 합니다. 실제로 이는 대기열의 헤드에서 대기열을 제거하는 것과 동일합니다.
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)
잠금 획득에 실패한 노드의 선행 노드 상태를 확인하여 SIGNAL로 업데이트하고, 현재 스레드를 차단해야 하는지 여부를 반환한다.
구현 논리: 전임 노드의 waitStatus를 결정하고 SIGNAL인 경우 true를 반환합니다. 0보다 크면 전임 노드가 취소되었으며 전임 노드의 waitStatus가 나올 때까지 루프에서 전임 노드를 건너뜁니다. <=0이면 false를 반환하고 그렇지 않으면 CAS 선행 노드의 waitStatus를 SIGNAL로 설정하고 false를 반환합니다.
개인 최종 부울 공원AndCheckInterrupt()
통화 대기, 인터럽트 상태 확인 및 반환
return thread.interrupted() 여기에 반환된 결과는 인터럽트로 인해 스레드가 활성화되었는지 여부를 결정하는 데 사용됩니다. 인터럽트로 인해 활성화된 경우 해당 스레드는 여전히 acquireQueued의 루프에 있으며 파크에 의해 다시 차단됩니다. Interrupted() 메서드는 인터럽트 플래그를 지워서 파크가 다시 적용될 수 있기 때문입니다. unpark에 의해 깨어나면 잠금이 획득됩니다. 외부 루프를 통해 다시 확인하세요.
잘못된 각성의 원인은 무엇입니까?
private void cancelAcquire(노드 노드)
전체 메서드가 예외를 throw할 때 호출되고, 노드에서 스레드를 비우고, 상태를 CANCELLED로 설정하고, waitStatus<=0이 있는 노드를 대기열에서 제거하고, 필요한 경우 다음 노드를 깨웁니다. 적합한 노드
개인 무효 unparkSuccessor(노드 노드)
다음 노드가 비어 있지 않으면 unpark하고, 다음 노드가 null이면 tail에서 시작하여 tail에서 다음 노드가 아닌 waitStatus<=0인 맨 앞의 노드를 찾습니다. 앞쪽.
뒤에서 앞으로 검색하는 이유는 이중 연결 리스트의 선행자와 후행자를 설정하는 것이 원자적 연산이 아니기 때문입니다. 노드의 다음 노드가 비어 있지만 이미 대기열에 있거나 노드가 방금 실행된 경우가 발생할 수 있습니다. 취소되었으며 다음 노드는 자신을 가리키며 순회는 이 노드로 이동합니다.
공개 최종 무효 획득Interruptously(int arg)
중단에 대한 응답으로 배타적 모드에서 동기화 상태를 얻으려면 Thread.interrupted()를 사용하여 tryAcqure 전에 중단되었는지 여부를 확인합니다. 그렇다면 예외가 발생합니다.
시도획득
일단 한번 해보세요
개인 무효 doAcquireInterruptible(int arg)
기본 로직은 acquireQueued와 동일합니다. 차이점은 addWaiter가 내부적으로 호출되어 스레드 중단 여부를 반환하지 않고 중단으로 인해 파크가 깨어났는지 확인한 후 InterruptedException을 직접 발생시킨다는 것입니다.
취소취득
공개 최종 부울 tryAcquireNanos(int arg, long nanosTimeout)
타임아웃으로 동기화 상태를 독점적으로 획득하여 인터럽트에 응답합니다. 타임아웃 기간 내에 동기화 상태를 획득하지 못한 경우 false를 반환합니다.
시도획득
일단 한번 해보세요
개인 부울 doAcquireNanos(int arg, long nanosTimeout)
기본 논리는 시간 판단 논리가 추가되고 spinForTimeoutThreshold 임계값이 사용되는 점을 제외하면 doAcquireInterruptible과 동일합니다.
취소취득
공개 최종 부울 릴리스(int arg)
Exclusive 모드는 동기화 상태를 해제합니다. 먼저 tryRelease를 호출하여 상태 수정을 시도합니다. 성공 후 head != null 및 head.waitStatus!=0으로 판단됩니다(여기서 head의 waitStatus는 0으로, head의 waitStatus가 비어 있음을 나타냄). 0이면 빈 대기열을 의미합니다. 호출되면 unparkSuccessor가 호출되어 후속 노드를 깨운 다음 true를 반환하고 false를 반환합니다.
보호된 부울 tryRelease(int arg)
상태를 수정해 보세요. AQS에는 특정 구현이 없으며 하위 클래스에서 직접 구현해야 합니다. 이 메서드에는 해제 메서드의 arg 변수가 사용됩니다. 반환 값은 상태가 성공적으로 해제되었는지 여부입니다.
arg에 대한 설명은 아래 구현 섹션을 참조하세요.
unpark후계자
공유 모드
여러 스레드가 동시에 동기화 상태를 얻습니다.
공개 최종 무효 acquireShared(int arg)
공유 모드는 동기화 상태를 획득하고 인터럽트를 무시합니다. 구현: 먼저 tryAcquireShared를 호출하여 동기화 상태를 얻으려고 합니다. 실패 후(결과가 0보다 작음) doAcquireShared를 호출하여 동기화 상태를 얻습니다.
protected int tryAcquireShared(int arg)
동기화 상태를 얻으십시오. AQS에서는 특정 메소드가 제공되지 않으며 서브클래스에 의해 구현됩니다. 0보다 작은 반환 값은 획득에 실패했음을 나타내고 0과 같으면 획득에 성공했지만 다른 공유 모드 획득에 성공하지 않았음을 나타냅니다. 0보다 큰 경우 획득에 성공했으며 다른 공유 모드 획득도 성공할 수 있음을 나타냅니다.
개인 무효 doAcquireShared(int arg)
큐에 합류한 후 spin은 동기화 상태를 얻으려고 시도합니다. 기본 논리는 배타적 모드의 acquireQueued와 동일합니다. 차이점은 노드의 선행자가 선두인 경우 tryAcquireShared가 반환한 결과를 판단해야 한다는 것입니다. 0보다 크거나 같습니다(아직 리소스가 있고 계속 전파될 수 있음을 나타냄). 그런 다음 setHeadAndPropagate를 호출하여 헤드 및 전파 속성을 설정합니다(새 헤드를 설정하고 후속 노드를 판단하고 적절한 경우 깨우기). ; 그렇지 않은 경우 후속 작업(파크 및 후속 인터럽트 판단 등)을 위해 shouldParkAfterFailedAcquire 및 parkAndCheckInterrupt를 호출해야 합니다. 또한, 인터럽트 상태를 설정하는 selfTninterrupt 메소드도 이 메소드에서 실행됩니다.
TryAcquireShared
노드의 선행 노드가 헤드인 경우 먼저 시도해 보십시오.
개인 무효 setHeadAndPropagate(노드 노드, int 전파)
의사소통의 구체화. 이 메소드가 실제로 확인하는 것은 현재 Lock을 획득한 노드의 후속 노드, 즉 한 번만 역방향으로 전파할 수 있다는 것입니다. 처음에는 왜 루프에서 거꾸로 깨어나는 것과 유사한 코드를 찾지 못했는지 궁금했는데 나중에 setHeadAndPropagate 자체가 for(;;)에 있다는 것을 알게 되었습니다. , 그래서 이것은 역방향 전파의 목적을 하나씩 달성합니다. 참고로 같이 깨어난 뒤 경쟁하는 것이 아니라 하나씩 깨어난다.
계속해서 뒤로 노드를 깨우세요. 이 메서드는 tryAcquireShared가 결과 r>=0(아직 사용 가능한 리소스가 있음을 나타냄)을 반환하고 r이 전파 매개 변수로 메서드에 전달되는 경우에만 호출됩니다. 이 메서드는 노드를 헤드로 설정한 다음, 뒤로 전달할 수 있는지 여부를 결정합니다. 그렇다면 doReleaseShared를 호출하여 후속 노드를 깨웁니다.
판단 논리: propagate>0(권한이 있음을 나타냄) 또는 이전 머리 == null 또는 이전 head.waitStatus < 0 아니면 현재 헤드(즉, 노드) == null 아니면 이제 head.waitStatus < 0 그런 다음 다음 단계로 이동합니다. node.next == 널 또는 node.next.isShared()
실제로는 그다지 명확하지 않습니다. 1. 현재 헤드((h = head) == null)를 결정해야 하는 이유는 무엇입니까? 2. node.next == null 이후에도 doReleaseShared가 필요한 이유
1의 추측: 판단 과정에서 헤드가 다른 스레드에 의해 수정될 수 있기 때문에 새 헤드가 여전히 이 조건을 충족하면 계속 진행할 수 있습니다. 여기서 판단 논리는 왜 헤드가 null인지 알 수 없는 것과 비슷하다고 생각합니다. 하지만 나는 여전히 보수적인 처리 전략을 시도합니다. 그렇다면 2는 유사한 보수적인 전략일 수 있습니다.
waitStatus<0이라고 판단할 뿐입니다. 이는 헤드 상태가 SIGNAL로 변경될 수 있기 때문이며 여기에는 CONDITION이 나타나지 않습니다.
개인 무효 doReleaseShared()
Spin은 후속 노드를 깨우는데, 이는 전파 속성도 보장해야 한다는 점에서 독립 모드와 다릅니다. 구현: 루프: 대기열이 비어 있지 않으면 head의 waitStatus를 확인합니다. SIGNAL과 같으면 CAS는 waitStatus를 0으로 설정합니다. 성공하면 unparkSuccessor(head)가 호출되어 실패하면 후속 노드를 깨웁니다. , 처음부터 다시 시작합니다. waitStatus는 0이고 CAS는 waitStatus를 0으로 설정합니다. PROPAGATE가 실패하면 다시 시작한 다음 head==h를 판단하여 헤드 노드가 수정되었는지 확인하고, 그렇다면 루프를 종료합니다.
이 메서드는 매개변수를 전달하지 않고 헤드에서 직접 시작된다는 점에 유의하세요.
추측: 공유 모드 노드에는 0(방금 생성됨), PROPAGATE 및 SIGNAL의 세 가지 상태만 있을 수 있습니다.
unpark후계자
취소취득
공개 최종 무효 acquireSharedInterruptible(int arg)
공유 모드는 배타적 모드와 유사한 방식으로 동기화 상태를 획득하고 인터럽트를 지원합니다.
TryAcquireShared
개인 무효 doAcquireSharedInterruptously(int arg)
doAcquireShared 논리와 유사하게 중단이 감지된 후 예외가 발생합니다.
취소취득
공개 최종 부울 tryAcquireSharedNanos(int arg, long nanosTimeout)
제한 시간이 있는 공유 모드는 동기화 상태를 획득하고 중단을 지원합니다.
TryAcquireShared
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
타임아웃 판정 로직은 Exclusive 모드와 유사하며, 기타 실행 로직은 doAcquireShared와 유사합니다.
취소취득
공개 최종 부울 releaseShared(int arg)
공유 모드는 동기화 상태를 해제합니다.
보호된 부울 tryReleaseShared(int arg)
서브클래스에 의해 구현된 빈 메서드는 동기화 상태를 해제하려고 시도합니다. arg는 releaseShared의 매개변수이며 직접 정의할 수 있습니다. 이 메소드가 성공적으로 동기화 상태를 해제하고 다른 스레드가 획득할 수 있으면 반환 값은 true이고, 그렇지 않으면 false를 반환합니다.
doReleaseShared
여기서는 여전히 후속 노드만 깨우고, 후속 깨우기 전파는 깨어난 노드에 의해 수행됩니다.
AQS를 기반으로 자체 잠금을 구현하는 방법
경쟁 자원
AQS는 개인 변수 상태를 사용하여 잠금 경쟁을 위한 리소스를 추상적으로 나타냅니다. 예를 들어 1은 점유됨을 의미하고 0은 점유되지 않음을 의미합니다.
상태에 대한 작업에는 getState, setState, CompareAndSetState 등의 관련 메서드를 사용해야 합니다.
주문형 구현 방법
tryAcquire, tryAcquireShared, tryRelease, tryReleaseShared, isHeldExclusively
AQS의 다양한 메소드의 arg 매개변수는 궁극적으로 이러한 메소드에 전달되므로 이러한 메소드는 arg 매개변수가 필요한지 여부와 이를 사용하는 방법을 결정합니다. 이는 소스 코드 주석에서 "무엇이든 나타낼 수 있다"는 의미입니다.
배타적 잠금(독립 모드)
protected boolean tryAcquire(int arg);
쿼리를 구현하고 경쟁력 있는 리소스를 얻는 것은 일반적으로 상태의 CAS 작업에 의해 완료됩니다. 들어오는 매개변수는 개발자가 정의할 수 있습니다.
간단한 예: protected boolean tryAcquire(int acquires) { Assert acquires == 1; // 들어오는 매개변수가 1만 될 수 있도록 정의합니다. if (비교AndSetState(0, 1)) { 사실을 반환; } 거짓을 반환; } protected boolean tryRelease(int releases) { Assert releases == 1; // 들어오는 매개변수가 1만 될 수 있도록 정의합니다. if (getState() == 0) throw new IllegalMonitorStateException(); 세트상태(0); 사실을 반환; }
보호된 부울 tryRelease(int arg);
경쟁 리소스를 해제하려면 일반적으로 상태 변수가 감소하거나 0으로 지워집니다. 들어오는 매개변수는 개발자가 정의할 수 있습니다.
메서드 내부에는 차단이나 대기가 없어야 합니다.
공유 잠금
protected int tryAcquireShared(int 획득)
들어오는 매개 변수는 개발자가 정의하며 획득하려는 리소스 수로 간주할 수 있습니다. 반환 값 int는 나머지 공유 리소스 수로 간주할 수 있습니다.
간단한 예: protected int tryAcquireShared(int acquires) {//불공정 을 위한 (;;) { int 사용 가능 = getState(); int 남은 = 사용 가능 - 획득; if (남은 < 0 || CompareAndSetState(사용 가능, 남음)) 남은 금액을 반환합니다. } } protected boolean tryReleaseShared(int releases) { 을 위한 (;;) { int 현재 = getState(); int next = 현재 릴리스; if(비교AndSetState(현재, 다음)) 사실을 반환; } }
protected boolean tryReleaseShared(int 릴리스)
들어오는 매개 변수는 개발자가 직접 정의하며 해제하려는 리소스의 수로 간주될 수 있습니다.
조건변수
AQS의 구현 로직에서는 상태를 사용하지 않지만 조건 변수를 사용할 때 기본적으로 상태는 릴리스의 수신 매개변수(int arg)로 사용되므로 이러한 메소드를 구현할 때 상태 변수를 수정해야 하는 경우가 많습니다.
보호된 부울 isHeldExclusively()
현재 스레드가 조건 변수에 해당하는 뮤텍스 잠금을 독점적으로 점유하는지 여부를 반환합니다.
일반적인 구현 방법: 보호된 부울 isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); } protected boolean tryAcquire(int acquires) { ... setExclusiveOwnerThread(Thread.currentThread()); 사실을 반환; }
재진입 읽기쓰기 잠금
재진입 잠금
소개
재진입 잠금은 공정한 잠금과 불공정한 잠금의 두 가지 방법으로 나뉩니다.
인터페이스 구현
잠그다
직렬화 가능
내부 클래스
추상 정적 클래스 동기화
추상 클래스는 ReetrantLock 구현의 기초입니다.
상속하다
AbstractQueuedSynchronizer
주요 방법
추상 무효 잠금()
잠금을 획득하는 추상 메서드인가요? 하위 클래스에서 구현해야 합니까?
최종 부울 nonfairTryAcquire(int 획득)
불공정한 tryLock 구현 구체적인 구현은 다음과 같습니다. 현재 잠금을 획득한 스레드가 없을 때 CAS는 잠금을 획득합니다. 성공하면 잠금 전용 스레드가 현재 스레드로 설정되고, 현재 스레드가 이미 잠금을 보유하고 있으면 현재 잠금 수가 업데이트됩니다. 이 두 경우에는 잠금이 유지되었음을 나타내는 true가 반환되고 다른 경우에는 false가 반환됩니다.
그렇다면 부모 클래스 Sync에 불공정한 구현을 배치하는 이유는 무엇입니까? 이 방법은 공정한 잠금과 불공정한 잠금에 사용되므로 두 가지 모두의 tryLock은 이 구현을 기반으로 합니다.
보호된 최종 부울 tryRelease(int 릴리스)
AQS에서 tryRelease를 구현하면 먼저 총 잠금 수에서 해제된 잠금 수를 뺀 값을 계산하여 남아 있는 잠금 수 c를 얻습니다. c가 0이면 상태를 c 값으로 설정하고 반환합니다. 잠금이 설정되어 있지 않은지 여부.
보호된 최종 부울 isHeldExclusively()
isHeldExclusively는 AQS에서 구현되어 getExclusiveOwnerThread() == Thread.currentThread()를 반환합니다.
최종 정수 getHoldCount()
현재 스레드가 보유하고 있는 잠금 수를 반환합니다. 이 방법은 isHeldExclusively의 판단에 의존합니다.
최종 부울 isLocked()
return getState() != 0; 스레드가 잠금을 보유하는지 여부
최종 스레드 getOwner()
잠금을 유지하는 스레드를 반환하거나, 그렇지 않은 경우 null을 반환합니다.
최종 ConditionObject newCondition()
새로운조건객체()
개인 무효 readObject(java.io.ObjectInputStream s)
역직렬화 방법, 상태를 0으로 설정
정적 최종 클래스 FairSync
공정한 잠금 구현 클래스
상속하다
동조
주요 방법
최종 무효 잠금()
획득(1)
여기서 매개변수 1은 CountDownLatch와는 다른 실용적인 의미를 갖습니다.
protected 최종 부울 tryAcquire(int 획득)
공정한 잠금 구현을 시도해 보세요. 구현 프로세스는 다음과 같습니다. 점유된 잠금 c가 0일 때 앞에 스레드가 없고 cas가 성공적으로 c를 획득하도록 설정한 경우 현재 스레드를 독점 스레드로 설정하고 현재 스레드가 이미 잠금을 보유하고 있으면 true를 반환합니다. 잠금 수를 업데이트하고 다른 모든 경우에는 false를 반환합니다.
hasQueued전임자
여기에는 불공정한 잠금보다 더 많은 판단이 있습니다. 현재 잠금을 획득한 스레드가 없더라도 오랫동안 대기 중인 스레드가 있으면 잠금 획득을 포기하게 됩니다.
정적 최종 클래스 NonfairSync
불공정한 잠금 구현 클래스
상속하다
동조
주요 방법
최종 무효 잠금()
먼저 직접 cas(0,1)을 시도합니다. 성공하면 현재 스레드가 잠금을 성공적으로 획득한 다음 실패하면 독점 스레드를 획득한 사람이 없음을 의미합니다. (1)
실제로 불공정 잠금의 tryAcquire 메서드도 cas를 시도합니다. 여기서 직접 CAS는 이전 호출이 리소스를 더 일찍 얻을 수 있음을 강조하는 것일 수 있습니다.
protected 최종 부울 tryAcquire(int 획득)
비공정 반환TryAcquire(획득) Sync에 불공정한 구현이 이미 존재합니다. 직접 호출하세요.
멤버변수
비공개 최종 동기화
잠금이 공정하게 구현되었는지, 불공정하게 구현되었는지를 나타내며 인스턴스화 후 FairSync 또는 NonfairSync에 해당합니다.
건설자
공개 재진입 잠금()
기본적으로 생성되는 불공정 잠금
공개 ReentrantLock(부울 공정)
공정한 선택에 따라 공정한 잠금 또는 불공정한 잠금을 구축합니다.
주요 방법
공개 무효 잠금()
동기화.잠금()
공개 무효 잠금 중단 없이()
sync.acquireInterruptically(1)
공개 부울 tryLock()
return sync.nonfairTryAcquire(1); 잠금이 성공적으로 획득되었거나 현재 스레드가 이미 잠금을 보유하고 있으면 true를 반환하고, 그렇지 않으면 false를 반환합니다.
여기서 자물쇠가 무엇이든 간에, 자물쇠를 얻으려는 것은 부당한 시도입니다.
public boolean tryLock(긴 시간 초과, TimeUnit 단위)
return sync.tryAcquireNanos(1, unit.toNanos(timeout))
tryAcquireNano는 tryAcquire를 호출하므로 공정성과 불공평성에 차이가 있습니다.
공개 무효 잠금 해제()
동기화.릴리스(1)
공개 조건 newCondition()
sync.newCondition()을 반환합니다.
공개 int getHoldCount()
return sync.getHoldCount() 현재 스레드가 보유한 잠금 수
공개 부울 isHeldByCurrentThread()
현재 스레드가 잠금을 보유하고 있는지 여부 sync.isHeldExclusively()를 반환합니다.
공개 부울 isLocked()
sync.isLocked()를 반환합니다.
공개 최종 부울 isFair()
FairSync의 동기화 인스턴스 반환
보호된 스레드 getOwner()
sync.getOwner()를 반환합니다.
공개 최종 부울 hasQueuedThreads()
대기열에 스레드가 있는지 여부에 따라 sync.hasQueuedThreads()를 반환합니다.
공개 최종 부울 hasQueuedThread(스레드 스레드)
return sync.isQueued(thread); 현재 스레드가 대기열에 있는지 여부
공개 최종 int getQueueLength()
return sync.getQueueLength();
보호된 컬렉션<Thread> getQueuedThreads()
return sync.getQueuedThreads();
public boolean hasWaiters(조건 조건)
주어진 조건에서 대기 중인 스레드가 있는지 여부(조건 큐에 CONDITION 상태의 스레드가 있는지 여부에 해당)
public int getWaitQueueLength(조건 조건)
주어진 조건에 대한 조건 큐에 있는 CONDITION 상태의 스레드 수
protected Collection<Thread> getWaitingThreads(조건 조건)
상태
소개
인터페이스, 조건 변수는 주로 특정 상태에 대한 스레드 실행의 의존성을 관리하고, 개체의 모니터 메서드(wait/notify/notifyAll)를 개체에 매핑하고, 잠금 구현을 사용하여 이를 결합하여 각 개체가 가지고 있는 것을 달성하는 데 사용됩니다. 다중 대기 세트의 효과. Lock이 동기화를 대체하는 것과 같고, Condition을 모니터 방식으로 대체하는 것과 동일합니다. 즉, Lock과 함께 Condition을 사용해야 합니다.
조건 메소드와 모니터 메소드의 주요 차이점은 다음과 같습니다. 1. 조건은 여러 대기 대기열을 지원하며 하나의 Lock 인스턴스는 여러 조건에 바인딩될 수 있습니다. 2. 조건은 응답 인터럽트 및 시간 초과 설정을 지원할 수 있습니다.
다중 조건 바인딩의 깊은 의미는 스레드가 다양한 상황에 따라 관리될 수 있다는 것입니다. 소위 "세밀한" 제어는 서로 다른 조건으로 인해 차단된 스레드가 서로 다른 조건 대기열에 있고 깨워야 할 때만 깨워진다는 것을 의미합니다. 경쟁하기 위해 동기화 대기열에 들어가면 자체적으로 차별화되고 격리됩니다.
이해하다
일반 잠금 경쟁과의 차이점은 조건이 다중 스레드 환경에서 조건을 충족해야 특정 작업을 수행할 수 있다는 점입니다. 반면 일반 잠금 경쟁은 특정 코드를 실행할 권리를 놓고 경쟁하는 것과 같습니다. .
일반적인 프로그래밍 모델: 잠금을 얻으십시오; while(조건부 상태가 만족되지 않음) { 잠금 해제; 스레드는 정지되고 알림 조건이 충족될 때까지 기다립니다. 잠금을 다시 획득합니다. } 중요 섹션 작업 잠금 해제;
삼항 관계: 잠금, 대기, 조건부 주장(술어). 각 대기 호출은 암시적으로 특정 조건부 주장과 연관되어 있습니다. 특정 조건부 주장에 대한 대기를 호출할 때 호출자는 이미 주장과 관련된 조건부 큐의 잠금을 보유해야 하며 이 잠금은 조건부 주장을 구성하는 조건을 보호해야 합니다. 상태변수
조건자: bool 유형을 반환하는 함수
만들다
Lock 인터페이스의 newCondition 메소드를 통해 생성되며 구현해야 하는 클래스는 자체 로직을 구현합니다(기본적으로 ConditionObject의 인스턴스 생성).
주요 방법
무효 대기()
스레드가 호출될 때 신호 또는 인터럽트에 의해 깨어날 때까지 기다리게 하면 조건과 관련된 잠금이 자동으로 해제되고 스레드는 signal/signalAll/인터럽트/가짜 깨우기에 의해 깨어날 때까지 현재 작업 실행을 중지합니다. 스레드는 대기 메서드를 호출하기 전에 조건과 관련된 잠금을 획득하기 위해 재시도해야 합니다.
boolean wait(오랜 시간, TimeUnit 단위)
waitNanos와 유사하게 시간 단위를 지정할 수 있으며, 시간이 소진되면 false를 반환하고, 그렇지 않으면 true를 반환합니다.
long waitNanos(long nanosTimeout)
Wait와 비슷하지만 늦어도 지정된 시간 이후에 깨어나고 사용하지 않은 시간을 반환합니다.
void waitUninterruptible();
Wait와 유사하지만 인터럽트에 응답하지 않습니다.
boolean waitUntil(날짜 기한)
AwaitNanos와 마찬가지로 시간을 지정하는 방법이 다릅니다. 마감일에 도달하면 false를 반환하고, 그렇지 않으면 true를 반환합니다.
무효 신호()
대기 상태의 스레드를 깨운 후 다시 잠금을 획득해야 합니다.
무효 signalAll()
모든 대기 스레드 깨우기
잠그다
소개
인터페이스는 잠금을 요청/해제하기 위한 몇 가지 일반적인 방법을 제공합니다.
동기화와의 비교
동기화 관련 메커니즘(예: 모니터 개체 등)은 내장되어 있으며 언어 수준에서 액세스할 수 없습니다. 잠금은 언어 수준의 대체 솔루션이므로 언어 수준의 편의를 얻을 수 있습니다(예: 잠금 획득 성공 여부 등)
동기화의 잠금 및 잠금 해제 프로세스는 키워드 자체에 의해 완료되는 반면 Lock은 잠금 및 잠금 해제 메서드를 명시적으로 호출해야 합니다.
잠금은 조건 변수를 사용하여 잠금을 보다 유연하게 사용할 수 있습니다.
잠금은 인터럽트에 응답할 수 있지만 동기화는 응답할 수 없습니다.
주요 방법
무효 잠금()
무효 잠금중단()
잠금 요청 프로세스 중에 인터럽트가 응답됩니다.
부울 tryLock()
잠금을 획득하고 결과를 직접 반환해 보십시오(현재 스레드를 차단하지 않고).
boolean tryLock(장시간, TimeUnit 단위)
시간 초과 및 응답 인터럽트가 포함된 tryLock
무효 잠금 해제();
조건 newCondition()
잠금과 관련된 Condition 객체를 반환합니다.
잠금지원
소개
잠금 및 기타 동기화 클래스를 생성하는 데 사용되는 기본 스레드 차단 기본 요소(허가 관련)
맨 아래 계층은 안전하지 않은 구현에 의존합니다.
공법
비공개, 이 클래스의 개체 구성은 허용되지 않습니다.
멤버변수
개인 정적 최종 sun.misc.Unsafe UNSAFE
정적 코드 블록으로 빌드
sun.기타.안전하지 않음
개인 정적 최종 긴 parkBlockerOffset
Unsafe를 통해 Thread 클래스 객체에서 parkBlocker 속성의 오프셋 위치를 얻습니다.
parkBlocker는 스레드가 차단된 경우의 기록으로, 스레드가 차단된 이유를 식별하기 위한 모니터링 및 진단 도구를 용이하게 합니다. 스레드가 차단되지 않은 경우에는 null입니다.
비공개 정적 최종 긴 SEED
Unsafe를 통해 Thread 클래스의 threadLocalRandomSeed 속성의 오프셋 위치를 얻습니다.
개인 정적 최종 긴 프로브
Unsafe를 통해 Thread 클래스의 threadLocalRandomProbe 속성의 오프셋 위치를 얻습니다.
비공개 정적 최종 긴 SECONDARY
Unsafe를 통해 Thread 클래스의 threadLocalRandomSecondarySeed 속성의 오프셋 위치를 얻습니다.
이 세 가지 속성은 ThreadLocalRandom에 의해 설정됩니다.
주요 방법
공개 정적 개체 getBlocker(스레드 t)
Unsafe를 통해 Thread 객체에서 parkBlocker 객체를 가져옵니다.
개인 정적 무효 setBlocker(Thread t, Object arg)
Private 메소드, Thread through Unsafe에서 parkBlocker 객체를 설정합니다. 차단기를 설정하는 다음 메소드는 이 메소드를 호출합니다.
공공 정적 무효 공원()
스레드를 차단하고 "허가"가 실행을 계속할 때까지 기다립니다.
특정 구현(핫스팟): Parker 클래스
具体的实现与平台相关,每个线程都有一个Parker实例,在linux实现中实际上是以mutex和condition最终实现了锁和阻塞的过程(具体看笔记文章,这里不写了)
主要成员变量(linux实现)
private volatile int _counter
表示许可,大于0表示已获取许可,每次park只会将其设置为1,不会递增
따라서 unpark를 두 번 호출한 다음 park를 두 번 호출하면 두 번째 시간은 여전히 차단됩니다.
protected pthread_mutex_t _mutex[1]
互斥变量
在代码中可以通过linux原子指令pthread_mutex_lock、pthread_mutex_trylock、pthread_mutex_unlock对变量进行加锁和解锁操作
protected pthread_cond_t _cond[1]
条件变量
利用线程间共享的全局变量进行同步的一种机制,一般和互斥锁结合使用(避免条件变量的竞争);可以通过pthread_cond_wait、pthread_cond_timedwait等指令进行操作
Linux 커널에는 차단된 프로세스를 관리하기 위한 대기 대기열도 있습니다.
方法
park
unpark
대기와의 사용 차이점
wait는 알림을 기다려야 깨어날 수 있습니다. 순차적인 프로세스가 있으며, park가 기다리고 있는 unpark는 park 이전에 호출될 수 있습니다. 이때 park 이후의 프로그램은 계속 실행될 수 있습니다.
기다려 개체의 모니터를 가져와야 하지만 파크는 그렇지 않습니다.
알아채다
공원에서 중단이 발생하면 공원도 반환되지만 중단 예외가 발생하지 않습니다.
공공 정적 무효 공원(객체 차단기)
parkBlocker의 역할 때문에 blocker 매개변수와 함께 park 메소드를 사용하는 것이 더 권장됩니다. 이를 사용한 후에는 대기할 파킹과 같은 jstack과 같은 진단 도구를 통해 스레드가 차단되는 프롬프트를 볼 수 있습니다. <0x000000045ed12298> (xxxx 객체의 모든 정규화된 이름)
public static void parkNanos(객체 차단기, 긴 나노스)
공공 정적 무효 공원나노스(롱 나노스)
프로그램이 계속 실행되기 전의 최대 차단 시간
public static void parkUntil(객체 차단기, 긴 기한)
public static void parkUntil(긴 기한)
공개 정적 무효 unpark(스레드 스레드)