ConcurrentLinkedQueue 비 차단 대기 열
9177 단어 자바자바 다 중 스 레 드
ConcurrentLinkedQueue 는 링크 노드 를 기반 으로 하 는 경계 없 는 스 레 드 보안 대기 열 로 FIFO 원칙 으로 요 소 를 정렬 합 니 다."wait-free"알고리즘(즉 CAS 알고리즘)을 사용 하여 이 루어 졌 습 니 다.
Coucurrent LinkedQueue 는 다음 과 같은 몇 가지 불변성 을 규정 했다.
head 의 불변성 과 가 변성:
tail 의 불변성 과 가 변성
이런 특성 들 은 이미 어 지 럽 지 않 습 니까?괜찮아,우 리 는 아래 의 소스 코드 분석 을 보면 이러한 특성 을 이해 할 수 있어.
ConcurrentLinkedQueue 소스 코드 분석
Coucurrent LinkedQueue 의 구 조 는 head 노드 와 tail 노드 로 구성 되 고 모든 노드 는 노드 요소 item 과 다음 노드 를 가리 키 는 next 참조 로 구성 되 며 노드 와 노드 간 의 관 계 는 이 next 를 통 해 연 결 된 것 으로 링크 의 대기 열 을 구성 합 니 다.노드 노드 는 ConcurrentLinkedQueue 의 내부 클래스 로 다음 과 같이 정의 합 니 다.
private static class Node {
/** */
volatile E item;
volatile Node next;
// , item next , CAS
Node(E item) {
UNSAFE.putObject(this, itemOffset, item);
}
boolean casItem(E cmp, E val) {
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
void lazySetNext(Node val) {
UNSAFE.putOrderedObject(this, nextOffset, val);
}
boolean casNext(Node cmp, Node val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
/** */
private static final long itemOffset;
/** */
private static final long nextOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class> k = Node.class;
itemOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("item"));
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
}
열거 하 다
열 에 들 어가 면 우 리 는 매우 간단 한 과정 이 라 고 생각한다.tail 노드 의 next 는 새로운 노드 를 실행 한 다음 에 tail 을 새로운 노드 로 업데이트 하면 된다.단일 스 레 드 각도 에서 우리 가 이렇게 이해 하 는 것 은 문제 가 없 을 것 이다.그러나 다 중 스 레 드 는?만약 에 하나의 스 레 드 가 삽입 동작 을 하고 있다 면 먼저 꼬리 노드 를 가 져 온 다음 에 꼬리 노드 의 다음 노드 를 현재 노드 로 설정 해 야 합 니 다.그러나 하나의 스 레 드 가 삽입 을 완 료 했 으 면 꼬리 노드 에 변화 가 생 겼 습 니까?이 경우 Concurrent LinkedQueue 는 어떻게 처리 하나 요?우 리 는 먼저 소스 코드 를 본다.
offer(E e):지정 한 요 소 를 도 큐 끝 에 삽입 합 니 다.
public boolean offer(E e) {
// null
checkNotNull(e);
//
final Node newNode = new Node(e);
//
for (Node t = tail, p = t;;) {
Node q = p.next;
// q == null p ,
// , p
if (q == null) { // --- 1
// casNext:t next
// casTail: tail
if (p.casNext(null, newNode)) { // --- 2
// node tail , tail
if (p != t) // --- 3
casTail(t, newNode); // --- 4
return true;
}
}
// p == q
else if (p == q) // --- 5
// p == q
// , offer() poll(), offer() poll()
// poll() updateHead() head q, p.next , :p.next == p
// tail head(tail head ), p
p = (t != (t = tail)) ? t : head; // --- 6
// tail
else
// tail , p
p = (p != t && t != (t = tail)) ? t : q; // --- 7
}
}
소스 코드 만 봐 도 헷 갈 려 서 노드 를 삽입 하면 한 번 분석 하면 밝 아 집 니 다.
초기 화
ConcurrentLinkedQueue 초기 화 시 head,tail 에 저 장 된 요 소 는 모두 null 이 고 head 는 tail 과 같 습 니 다.
원소 추가 A
프로그램 분석 에 따 르 면 첫 번 째 요소 A,head=tail=dummy Node,모든 q=p.next=null,직접 2:p.casNext(null,new Node)를 삽입 합 니 다.p==t 가 성립 되 기 때문에 절차 3:casTail(t,new Node)을 실행 하지 않 고 직접 return 합 니 다.A 노드 를 삽입 하면 다음 과 같 습 니 다.
원소 추가 B
q=p.next=A,p=tail=dummy Node,그래서 바로 7:p=(p!=t && t != (t = tail)) ? t : q;。이때 p=q,그리고 두 번 째 순환 q=p.next=null,절차 2:p==null 이 성립 되 고 이 노드 를 삽입 합 니 다.p=q,t=tail 이기 때문에 절차 3:p!=t 성립,집행 절차 4:casTail(t,newNode),그리고 return.다음 과 같다.
노드 C 추가
이때 t=tail,p=t,q=p.next=null 은 요소 A 를 삽입 하 는 것 과 다 름 이 없습니다.다음 과 같 습 니 다.
여기 서 전체 offer()과정 은 이미 분석 이 끝 났 습 니 다.아마도 p==q 는 이해 하기 어 려 울 것 입 니 다.p 는 q.next 와 같 지 않 습 니까?어떻게 p==q 가 있 습 니까?이 의문 은 우리 가 열 거 된 poll()에서 분석 합 니 다.
열거 하 다
Concurrent LinkedQueue 는 poll()방법 을 제공 하여 열 을 표시 합 니 다.입 열 은 주로 tail 과 관련 되 고,출 열 은 head 와 관련된다.우 리 는 먼저 소스 코드 를 본다.
public E poll() {
// p head
restartFromHead: // ?
for (;;) {
for (Node h = head, p = h, q;;) {
// item
E item = p.item;
// item null, item null
if (item != null && p.casItem(item, null)) { // --- 1
// p != head head
if (p != h) // --- 2
// p.next != null, head p.next , p
updateHead(h, ((q = p.next) != null) ? q : p); // --- 3
return item;
}
// p.next == null
else if ((q = p.next) == null) { // --- 4
updateHead(h, p);
return null;
}
// poll , p —— p.next = p,p ,
else if (p == q) // --- 5
continue restartFromHead;
else
p = q; // --- 6
}
}
}
이것 은 offer()방법 에 비해 간단 할 것 입 니 다.그 안에 중요 한 방법 이 있 습 니 다.updateHead().이 방법 은 CAS 에서 head 노드 를 업데이트 하 는 데 사 용 됩 니 다.다음 과 같 습 니 다.
final void updateHead(Node h, Node p) {
if (h != p && casHead(h, p))
h.lazySetNext(h);
}
우 리 는 먼저 위의 offer()의 링크 poll()을 떨 어 뜨리 고 A,B,C 노드 구 조 를 다음 과 같이 추가 합 니 다.
poll A
head=dumy,p=head,item=p.item=null,절차 1 이 성립 되 지 않 습 니 다.절차 4:(q=p.next)==null 이 성립 되 지 않 습 니 다.p.next=A,단계 6,다음 순환 으로 넘 어 갑 니 다.이때 p=A,그래서 절차 1 item!=null,p.casItem(item,null)진행 성공,이때 p==A!=h,그래서 실행 절차 3:updateHead(h,(q=p.next)!=null) ? q : p),q = p.next = B != null,head CAS 를 B 로 업데이트 합 니 다.다음 과 같 습 니 다.
poll B
head=B,p=head=B,item=p.item=B,절차 성립,절차 2:p!=h.성립 되 지 않 고 직접 return,다음 과 같 습 니 다.
poll C
head=dumy,p=head=dumy,tiem=p.item=null,절차 1 이 성립 되 지 않 습 니 다.절차 4:(q=p.next)==null,성립 되 지 않 습 니 다.그리고 절차 6 으로 넘 어 갑 니 다.이때 p=q=C,item=C(item),절차 1 이 성립 되 기 때문에 C(item)는 null,절차 2:p 로 설정 합 니 다!=h 성립,실행 절차 3:updateHead(h,(q=p.next)!=null) ? q:p),다음 과 같다.
여기 서 한눈 에 알 수 있 는 지,여기 서 우 리 는 offer()의 절차 5 를 분석 합 니 다.
else if(p == q){
p = (t != (t = tail))? t : head;
}
Concurrent LinkedQueue 에 따 르 면 p==q 는 이 노드 가 삭제 되 었 음 을 나타 낸다.즉,tail 은 head 보다 뒤 처 져 있 고 head 는 succ()방법 으로 tail 에 옮 겨 다 닐 수 없다.어떻게 합 니까?t != (t = tail))? t : head;(이 코드 의 가 독성 이 너무 떨 어 집 니 다.정말 이해 하기 어렵 습 니 다.t 로 이해 할 수 있 을 지 모 르 겠 습 니 다!=tail ? tail:head)이 코드 는 주로 tail 노드 가 바 뀌 었 는 지 확인 하 는 것 입 니 다.만약 에 변화 가 발생 하면 tail 이 다시 위 치 를 정 했 음 을 설명 합 니 다.tail 을 다시 찾 으 면 됩 니 다.그렇지 않 으 면 head 만 가리 킬 수 있 습 니 다.
위 에 있 는 그 요소 D 를 다시 삽입 합 니 다.p=head,q=p.next=null,실행 절차 1:q=null 및 p!=t,그래서 실행 절차 4:다음 과 같 습 니 다.
원소 E,q=p.next=null,p==t 를 삽입 하기 때문에 E 를 삽입 하면 다음 과 같 습 니 다.
여기까지 Concurrent LinkedQueue 의 전체 입 열,출 열 을 분 석 했 습 니 다.Concurrent LinkedQueue LZ 에 대해 진심으로 알 아 보기 어렵 고 알 아 본 후에 도 디자인 이 너무 정교 하 다 고 감탄 합 니 다.CAS 를 이용 하여 데이터 조작 을 완성 하 는 동시에 대열 의 불일치 성 을 허용 합 니 다.이런 약 한 일치 성 은 정말 강하 습 니 다.Doug Lea 의 천재 에 다시 한 번 감 탄 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Is Eclipse IDE dying?In 2014 the Eclipse IDE is the leading development environment for Java with a market share of approximately 65%. but ac...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.