Java JUC 학습 - ConcurrentLinkedDeque 상세 설명
선언
//C Source Code
//
LinkNode *SearchNode(LinkList *list, int i);
//
void LinkListInsert(LinkList *list, LinkNode *node, int i) {
//
LinkNode *previous = SearchNode(list, i-1);
//
node->next = previous->next;
previous->next = node;
}
ConcurrentLinkedDeque
유형 이다.0x 01 ConcurrentLinkedDeque 개요
ConcurrentLinkedDeque
는 자바 SE 7 / JDK 7 부터 제공 하 는 Non - blocking Thread - safe List (비 차단 식 스 레 드 보안 링크) 로 java.util.concurrent
가방 에 속 합 니 다.그 유형의 원형 은 다음 과 같다. public class ConcurrentLinkedDeque
extends AbstractCollection
implements Deque, Serializable
ConcurrentLinkedDeque
에 대한 표현 은 다음 과 같다. ConcurrentLinkedDeque
is an appropriate choice when many threads will share access to a common collection. Like most other concurrent collection implementations, this class does not permit the use of null elements. ConcurrentLinkedDeque
은 링크 노드 를 바탕 으로 하 는 무한 병발 링크 이다.삽입, 삭제, 접근 작업 을 안전하게 병행 할 수 있 습 니 다.많은 스 레 드 가 공공 집합 을 동시에 방문 할 때 ConcurrentLinkedDeque
는 적당 한 선택 이다.대부분의 다른 병렬 집합 유형 과 마찬가지 로 이 종 류 는 빈 요 소 를 사용 할 수 없습니다.size
방법 은 상수 적 인 조작 이 아니다.링크 의 비동기 성 때문에 현재 요소 의 수량 을 확인 하려 면 모든 요 소 를 옮 겨 다 녀 야 하기 때문에 옮 겨 다 니 는 동안 다른 스 레 드 가 이 집합 을 수정 하면 size
방법 은 정확 하지 않 은 결 과 를 보고 할 수 있 습 니 다.addAll(java.util.Collection extends E>)
, removeIf(java.util.function.Predicate super E>)
or forEach(java.util.function.Consumer super E>)
, are not guaranteed to be performed atomically. addAll(java.util.Collection extends E>)
, removeIf(java.util.function.Predicate super E>)
또는 removeIf(java.util.function.Predicate super E>)
또는 forEach(java.util.function.Consumer super E>)
방법 을 포함 하 는데 이 유형 은 원자 방식 으로 집행 하 는 것 을 보장 하지 않 는 다.원자 접근 을 보장 하려 면 대량 조작 방법 을 사용 해 서 는 안 된다 는 것 을 알 수 있다.ConcurrentLinkedDeque
는 본질 적 으로 도 하나의 링크 이다. 이 유형 에 대한 기본 적 인 조작 도 기본 적 인 링크 와 마찬가지 로 대체적으로 '생 성, 삽입, 삭제, 옮 겨 다 니 기, 비우 기' 등 이다. 다음은 구체 적 인 예 를 통 해 설명 한다.0x 02 ConcurrentLinkedDeque 소스 코드 분석
static final class Node {
// ,Java , 。
volatile Node prev;
// 。
volatile E item;
//
volatile Node next;
}
java.util.Deque
중의 인터페이스 방법 이다./**
* Returns {@code true} if this deque contains the specified element.
* More formally, returns {@code true} if and only if this deque contains
* at least one element {@code e} such that {@code o.equals(e)}.
*
* @param o element whose presence in this deque is to be tested
* @return {@code true} if this deque contains the specified element
*/
public boolean contains(Object o) {
// ,
if (o != null) {
// for , 。
for (Node p = first(); p != null; p = succ(p)) {
final E item;
// , true
if ((item = p.item) != null && o.equals(item))
return true;
}
}
return false;
}
/**
* Returns {@code true} if this collection contains no elements.
*
* @return {@code true} if this collection contains no elements
*/
public boolean isEmpty() {
// , peekFirst()
return peekFirst() == null;
}
/**
* Returns the number of elements in this deque. If this deque
* contains more than {@code Integer.MAX_VALUE} elements, it
* returns {@code Integer.MAX_VALUE}.
*
* Beware that, unlike in most collections, this method is
* NOT a constant-time operation. Because of the
* asynchronous nature of these deques, determining the current
* number of elements requires traversing them all to count them.
* Additionally, it is possible for the size to change during
* execution of this method, in which case the returned result
* will be inaccurate. Thus, this method is typically not very
* useful in concurrent applications.
*
* @return the number of elements in this deque
*/
public int size() {
// Java (label) ,
restartFromHead: for (;;) {
//count
int count = 0;
//
for (Node p = first(); p != null;) {
//
if (p.item != null)
// count 1
if (++count == Integer.MAX_VALUE)
break; // @see Collection.size()
// p p,
if (p == (p = p.next))
continue restartFromHead;
}
return count;
}
}
public E peekFirst() {
// ,
for (Node p = first(); p != null; p = succ(p)) {
final E item;
if ((item = p.item) != null)
return item;
}
return null;
}
public E peekLast() {
// ,
for (Node p = last(); p != null; p = pred(p)) {
final E item;
if ((item = p.item) != null)
return item;
}
return null;
}
protected
방법 과 private
방법 을 소개 한다.여기 서 첫 번 째 결점 에 삽입 하 는 방법 과 끝 점 에 삽입 하 는 방법 만 설명 하고 다른 방법 은 생각 이 대체적으로 같 으 므 로 여 기 는 더 이상 소개 하지 않 겠 습 니 다. /**
* Links e as first element.
*/
private void linkFirst(E e) {
// , 。
final Node newNode = newNode(Objects.requireNonNull(e));
restartFromHead:
for (;;)
//
for (Node h = head, p = h, q;;) {
// p
if ((q = p.prev) != null &&
(q = (p = q).prev) != null)
// Check for head updates every other hop.
// If p == q, we are sure to follow head instead.
p = (h != (h = head)) ? h : q;
else if (p.next == p) // PREV_TERMINATOR
continue restartFromHead;
else {
// p is first node
// p
NEXT.set(newNode, p); // CAS piggyback
//
if (PREV.compareAndSet(p, null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this deque,
// and for newNode to become "live".
if (p != h) // hop two nodes at a time; failure is OK
//
HEAD.weakCompareAndSet(this, h, newNode);
return;
}
// Lost CAS race to another thread; re-read prev
}
}
}
/**
* Links e as last element.
*/
private void linkLast(E e) {
// , 。
final Node newNode = newNode(Objects.requireNonNull(e));
restartFromTail:
for (;;)
for (Node t = tail, p = t, q;;) {
// p
if ((q = p.next) != null &&
(q = (p = q).next) != null)
// Check for tail updates every other hop.
// If p == q, we are sure to follow tail instead.
p = (t != (t = tail)) ? t : q;
else if (p.prev == p) // NEXT_TERMINATOR
continue restartFromTail;
else {
// p is last node
// p
PREV.set(newNode, p); // CAS piggyback
if (NEXT.compareAndSet(p, null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this deque,
// and for newNode to become "live".
if (p != t) // hop two nodes at a time; failure is OK
//
TAIL.weakCompareAndSet(this, t, newNode);
return;
}
// Lost CAS race to another thread; re-read next
}
}
}
0x 03 Concurrent LinkedDeque 의 실제 응용
package cn.xiaolus.cld;
import java.util.concurrent.ConcurrentLinkedDeque;
public class CLDMain {
private static ConcurrentLinkedDeque cld = new ConcurrentLinkedDeque<>();
public static void main(String[] args) {
int numThread = Runtime.getRuntime().availableProcessors();
Thread[] threads = new Thread[numThread];
for (int i = 0; i < threads.length; i++) {
(threads[i] = new Thread(addTask(), "Thread "+i)).start();
}
}
public static Runnable addTask() {
return new Runnable() {
@Override
public void run() {
int num = Runtime.getRuntime().availableProcessors();
for (int i = 0; i < num; i++) {
StringBuilder item = new StringBuilder("Item ").append(i);
cld.addFirst(item.toString());
callbackAdd(Thread.currentThread().getName(), item);
}
callbackFinish(Thread.currentThread().getName());
}
};
}
public static void callbackAdd(String threadName, StringBuilder item) {
StringBuilder builder = new StringBuilder(threadName).append(" added :").append(item);
System.out.println(builder);
}
public static void callbackFinish(String threadName) {
StringBuilder builder = new StringBuilder(threadName).append(" has Finished");
System.out.println(builder);
System.out.println(new StringBuilder("CurrentSize ").append(cld.size()));
}
}
Thread 0 added :Item 0
Thread 0 added :Item 1
Thread 0 added :Item 2
Thread 0 added :Item 3
Thread 0 has Finished
CurrentSize 6
Thread 1 added :Item 0
Thread 2 added :Item 0
Thread 2 added :Item 1
Thread 2 added :Item 2
Thread 2 added :Item 3
Thread 1 added :Item 1
Thread 1 added :Item 2
Thread 2 has Finished
Thread 1 added :Item 3
Thread 1 has Finished
CurrentSize 13
CurrentSize 13
Thread 3 added :Item 0
Thread 3 added :Item 1
Thread 3 added :Item 2
Thread 3 added :Item 3
Thread 3 has Finished
CurrentSize 16
ConcurrentLinkedDeque
의 전형 적 인 사용 장면 이다.또한 위의 설 도 검증 했다. 즉 size()
방법 은 링크 를 옮 겨 다 니 며 잘못된 결 과 를 되 돌 릴 수 있다.이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JAVA 다 중 스 레 드 메커니즘 의 스 레 드 생 성target 을 실행 대상 으로 지정 한 name 을 이름 으로 하고 group 에서 참조 하 는 스 레 드 그룹의 일원 으로 새 Thread 대상 을 할당 합 니 다. 이 스 레 드 가 독립 된 Runnable 실...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.