자바 병렬 시리즈 의 AbstractQueuedSynchronizer 소스 코드 분석(독점 모델)
11716 단어 Java병발 하 다AbstractQueuedSynchronizer
1.어떻게 응답 하지 않 는 라인 으로 자 물 쇠 를 가 져 옵 니까?
// ( )
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
selfInterrupt();
}
}
위의 코드 는 간단 해 보이 지만 다음 그림 에서 보 여 준 4 단 계 를 순서대로 수행 했다.다음은 단계별 로 시연 분석 을 진행 하 겠 습 니 다.STEP 1:!tryAcquire(arg)
// ( )
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
이때 한 사람 이 왔 다.그 는 먼저 문 을 두 드 려 보 았 다.문 이 잠 겨 있 지 않 은 것 을 발견 하면 바로 들 어 갔다.문 이 잠 겨 있 는 것 을 발견 하면 다음 단 계 를 수행 합 니 다.이 try Acquire 방법 은 언제 잠 겨 있 는 지,언제 잠 겨 있 는 지 를 결정 합 니 다.이 방법 은 하위 클래스 를 덮어 쓰 고 판단 논 리 를 다시 써 야 한다.두 번 째 단계:addWaiter(Node.EXCLUSIVE)
//
private Node addWaiter(Node mode) {
//
Node node = new Node(Thread.currentThread(), mode);
//
Node pred = tail;
// ,
if (pred != null) {
//1.
node.prev = pred;
//2.
if (compareAndSetTail(pred, node)) {
//3.
pred.next = node;
return node;
}
}
//
enq(node);
return node;
}
//
private Node enq(final Node node) {
for (;;) {
//
Node t = tail;
//
if (t == null) {
//
if (compareAndSetHead(new Node())) {
tail = head;
}
} else {
//1.
node.prev = t;
//2.
if (compareAndSetTail(t, node)) {
//3.
t.next = node;
return t;
}
}
}
}
이 단 계 를 실행 하면 처음으로 자 물 쇠 를 가 져 오 는 데 실 패 했 음 을 나타 낸다.그러면 이 사람 은 자신 에 게 번호 표를 받 고 줄 을 섰 다.번호 표를 받 을 때 자신 이 어떤 방식 으로 방 을 점용 하고 싶 은 지 설명 한다(독점 모드 or 공유 모드).주의 하 세 요.이때 그 는 앉 아서 쉬 지 않 았 습 니 다.STEP 3:acquireQueued(addWaiter(Node.EXCLUSIVE),arg)
// ( )
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//
final Node p = node.predecessor();
// ,
if (p == head && tryAcquire(arg)) {
// head
setHead(node);
// , head
p.next = null;
//
failed = false;
// ,
return interrupted;
}
// ,
// , ,
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
interrupted = true;
}
}
} finally {
//
if (failed) {
cancelAcquire(node);
}
}
}
//
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//
int ws = pred.waitStatus;
// SIGNAL, ,
if (ws == Node.SIGNAL) {
return true;
}
if (ws > 0) {
//
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// SIGNAL, 0,
// SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
//
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
번호표 받 고 줄 서기 구역 에 들 어가 면 바로 이 방법 을 집행 한다.한 노드 가 처음으로 줄 서기 구역 에 들 어간 후에 두 가지 상황 이 있다.하 나 는 그의 앞 에 있 는 사람 이 이미 자 리 를 떠 나 방 에 들 어간 것 을 발견 하면 그 는 앉 아서 쉬 지 않 고 다시 문 을 두 드 려 그 녀석 이 일 을 끝 냈 는 지 볼 것 이다.만약 안에 있 는 사람 이 마침 일 을 끝내 고 나 왔 다 면,그 가 자신 을 부 르 지 않 고 바로 뛰 어 들 었 을 것 이다.그렇지 않 으 면 앉 아서 좀 쉬 는 것 을 고려 해 야 하 는데,그 는 여전히 마음 이 놓 이지 않 는 다.만약 그 가 앉 아서 잠 든 후에 아무 도 그 에 게 어떻게 하 라 고 일 깨 워 주지 않 는 다 면?그 는 안에서 나 온 사람 이 쪽 지 를 보고 깨 울 수 있 도록 앞 자리 에 작은 쪽 지 를 남 겼 다.또 한 가지 상황 은 그 가 줄 을 서 있 는 구역 에 들 어간 후에 앞 에 몇 사람 이 자리 에서 줄 을 서 있 는 것 을 발견 하면 안심 하고 앉 아서 잠시 미 소 를 지 을 수 있 지만 그 전에 그 는 앞 에 있 는 사람(이때 이미 잠 들 었 다)의 자리 에 쪽 지 를 남 겨 서 이 사람 이 가기 전에 자신 을 깨 울 수 있 도록 하 는 것 이다.모든 일이 다 끝 난 후에 그 는 편안하게 잠 을 잤 다.우 리 는 전체 for 순환 이 하나의 출구 만 있 는 것 을 보 았 다.그것 은 바로 스 레 드 가 성공 적 으로 자 물 쇠 를 얻 은 후에 나 갈 수 있 고 자 물 쇠 를 얻 지 못 하기 전에 for 순환 의 Park AndCheck Interrupt()방법 에 걸 려 있 었 다.스 레 드 가 깨 어 난 후에 도 이곳 에서 for 순환 을 계속 실행 합 니 다.STEP 4:selfInterrupt()
//
private static void selfInterrupt() {
Thread.currentThread().interrupt();
}
위의 전체 스 레 드 는 for 순환 하 는 파 크 앤 드 체크 인 터 럽 트()방법 에 걸 려 있 기 때문에 자 물 쇠 를 얻 기 전에 어떠한 형식의 스 레 드 인 터 럽 트 에 도 응답 하지 않 습 니 다.스 레 드 가 자 물 쇠 를 성공 적 으로 얻 고 for 순환 에서 나 온 후에 야 스 레 드 를 중단 하 라 는 사람 이 있 는 지 확인 할 수 있 습 니 다.그렇다면 self Interrupt()방법 을 사용 하여 자신 을 걸 수 있 습 니 다.2.어떻게 응답 스 레 드 인 터 럽 트 로 자 물 쇠 를 가 져 옵 니까?
// ( )
private void doAcquireInterruptibly(int arg) throws InterruptedException {
//
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
//
final Node p = node.predecessor();
// p head ,
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
//
return;
}
// ,
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
//
throw new InterruptedException();
}
}
} finally {
if (failed) {
cancelAcquire(node);
}
}
}
응답 스 레 드 인 터 럽 트 방식 과 응답 하지 않 는 스 레 드 인 터 럽 트 방식 으로 잠 금 을 가 져 오 는 절 차 는 대체적으로 같 습 니 다.유일한 차이 점 은 스 레 드 가 파 크 앤 드 체크 인 터 럽 트 방법 에서 깨 어 난 후에 스 레 드 가 중단 되 었 는 지 확인 하 는 것 입 니 다.그렇다면 Interrupted Exception 이상 을 던 지고 스 레 드 중단 에 응 하지 않 고 자 물 쇠 를 가 져 오 는 것 은 중단 요청 을 받 은 후에 중단 상 태 를 설정 하 는 것 입 니 다.현재 자 물 쇠 를 가 져 오 는 방법 을 즉시 끝내 지 않 습 니 다.자 물 쇠 를 성공 적 으로 가 져 올 때 까지 중단 상태 에 따라 자신 을 걸 지 여 부 를 결정 합 니 다.3.시간 초과 로 자 물 쇠 를 가 져 오 는 방법 을 설정 합 니까?
// ( )
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
//
long lastTime = System.nanoTime();
//
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
//
final Node p = node.predecessor();
// head ,
if (p == head && tryAcquire(arg)) {
// head
setHead(node);
p.next = null;
failed = false;
return true;
}
//
if (nanosTimeout <= 0) {
return false;
}
// ,
if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) {
// ,
LockSupport.parkNanos(this, nanosTimeout);
}
//
long now = System.nanoTime();
//
nanosTimeout -= now - lastTime;
// lastTime
lastTime = now;
//
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
} finally {
if (failed) {
cancelAcquire(node);
}
}
}
시간 초과 가 져 오기 설정 을 하면 먼저 자 물 쇠 를 가 져 옵 니 다.첫 번 째 자 물 쇠 를 가 져 오 는 데 실패 한 후 상황 에 따라 시간 초과 가 자전 시간 보다 많 으 면 스 레 드 를 일정 시간 걸 고 그렇지 않 으 면 자전 을 합 니 다.자 물 쇠 를 가 져 올 때마다 시간 초과 시간 을 한 번 의 자 물 쇠 를 가 져 오 는 데 걸 리 는 시간 을 줄 입 니 다.시간 이 0 보다 적 을 때 까지 시간 이 초과 되 었 다 는 것 은 시간 이 다 되 었 다 는 것 을 의미 합 니 다.그러면 이 때 는 자 물 쇠 를 가 져 오 는 작업 을 마치 고 실패 표 지 를 가 져 옵 니 다.시간 초과 로 자 물 쇠 를 가 져 오 는 과정 에서 스 레 드 중단 요청 에 응답 할 수 있 음 을 주의 하 십시오.4.스 레 드 가 자 물 쇠 를 풀 고 동기 화 대기 열 을 떠 나 는 것 은 어떻게 진행 되 나 요?
// ( )
public final boolean release(int arg) {
// ,
if (tryRelease(arg)) {
// head
Node h = head;
// head 0
if (h != null && h.waitStatus != 0) {
//
unparkSuccessor(h);
}
return true;
}
return false;
}
//
private void unparkSuccessor(Node node) {
//
int ws = node.waitStatus;
// 0
if (ws < 0) {
compareAndSetWaitStatus(node, ws, 0);
}
//
Node s = node.next;
//
if (s == null || s.waitStatus > 0) {
s = null;
//
for (Node t = tail; t != null && t != node; t = t.prev) {
if (t.waitStatus <= 0) {
s = t;
}
}
}
//
if (s != null) {
LockSupport.unpark(s.thread);
}
}
스 레 드 는 자 물 쇠 를 가지 고 방 에 들 어가 면 자신의 일 을 하고 일이 끝 난 후에 자 물 쇠 를 풀 고 방 을 떠난다.tryRelease 방법 을 통 해 암호 자 물 쇠 를 눌 러 잠 금 을 풀 수 있 습 니 다.tryRelease 방법 은 하위 클래스 를 덮어 야 한 다 는 것 을 알 고 있 습 니 다.서로 다른 하위 클래스 가 실현 하 는 규칙 이 다 릅 니 다.즉,서로 다른 하위 클래스 가 설정 한 암호 가 다르다 는 것 입 니 다.ReentrantLock 에서 방 안에 있 는 사람 이 try Release 방법 을 호출 할 때마다 state 는 1 을 줄 이 고 state 가 0 으로 줄 어 들 때 까지 비밀번호 자물쇠 가 열 렸 다.여러분 은 이 과정 이 우리 가 끊임없이 비밀번호 자 물 쇠 를 돌 리 는 회전 바퀴 와 같 지 않 고 매번 회전 바퀴 숫자 가 1 만 줄 어드 는 것 과 같다 고 생각 하 세 요.Count Downlatch 는 이것 과 비슷 합 니 다.단지 한 사람 이 돌아 가 는 것 이 아니 라 여러 사람 이 돌아 가 는 것 입 니 다.여러분 의 힘 을 모 아 자 물 쇠 를 열 었 습 니 다.스 레 드 가 방 을 나 간 후에 그것 은 자신의 원래 의 자 리 를 찾 을 것 이다.즉,헤드 노드 를 찾 는 것 이다.자리 에 작은 쪽 지 를 남 긴 사람 이 있 는 지 살 펴 보 자.누 군가 잠 들 었 다 는 것 을 알 고 깨 워 달라 고 하면 그 스 레 드 를 깨 울 것 이다.없 으 면 동기 화 대기 열 에서 아직 기다 리 는 사람 이 없고 깨 워 야 할 사람 도 없 기 때문에 안심 하고 떠 날 수 있다 는 뜻 이다.이상 의 과정 은 독점 모드 에서 자 물 쇠 를 풀 어 주 는 과정 이다.주의:상기 모든 분석 은 JDK 1.7 을 바탕 으로 버 전 간 에 차이 가 있 을 수 있 으 므 로 독자 들 은 주의해 야 합 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.