Java에서 어떤 동시 큐 구현을 사용해야합니까?


132

JavaDocs에서 :

  • ConcurrentLinkedQueue를이 많은 스레드가 공통의 컬렉션에 대한 액세스를 공유 적절한 선택이 될 것입니다. 이 큐는 널 요소를 허용하지 않습니다.
  • ArrayBlockingQueue 는 고정 된 크기의 배열이 생산자가 삽입하고 소비자가 추출한 요소를 보유하는 클래식 "바운드 버퍼"입니다. 이 클래스는 대기중인 생산자 및 소비자 스레드 주문에 대한 선택적 공정성 정책을 지원합니다.
  • LinkedBlockingQueue는 일반적으로 어레이 기반 대기열보다 처리량이 많지만 대부분의 동시 응용 프로그램에서 예측 가능한 성능은 떨어집니다.

두 가지 시나리오가 있습니다. 하나는 하나의 소비자와 많은 생산자 (이를 사용하는 스레드)를 지원하기 위해 대기열이 필요하고 다른 하나는 다른 방법입니다.

사용할 구현을 이해하지 못합니다. 차이점이 무엇인지 누군가가 설명 할 수 있습니까?

또한 '선택적 공정성 정책' ArrayBlockingQueue이란 무엇입니까?


1
스레드가 처리되는 순서를 지정하는 데 유용한 PriorityBlockingQueue에 대해서도 묻지 않았습니다.
IgorGanapolsky

답변:


53

기본적으로 이들의 차이점은 성능 특성과 차단 동작입니다.

가장 쉬운 방법 ArrayBlockingQueue은 고정 된 크기의 대기열입니다. 따라서 크기를 10으로 설정하고 11 번째 요소를 삽입하려고하면 다른 스레드가 요소를 제거 할 때까지 insert 문이 차단됩니다. 공정성 문제는 여러 스레드가 동시에 삽입 및 제거하려고 시도하는 경우 (즉, 큐가 차단 된 기간 동안) 발생합니다. 공정성 알고리즘은 요청한 첫 번째 스레드가 첫 번째 스레드가되도록합니다. 그렇지 않으면, 주어진 스레드가 다른 스레드보다 더 오래 대기하여 예기치 않은 동작을 유발할 수 있습니다 (때때로 시작된 다른 스레드가 먼저 처리되기 때문에 한 스레드가 몇 초 정도 걸리기도합니다). 단점은 공정성을 관리하는 데 오버 헤드가 발생하여 처리량이 느려진다는 것입니다.

가장 중요한 차이점 LinkedBlockingQueueConcurrentLinkedQueue당신이에서 요소를 요청하는 경우이다 LinkedBlockingQueue큐가 비어있는 무언가가있을 때까지, 당신의 스레드가 대기합니다. ConcurrentLinkedQueue빈 대기열의 동작으로 A 가 즉시 반환됩니다.

차단이 필요한지 여부에 따라 다릅니다. 많은 생산자와 하나의 소비자가있는 경우에는 그 소리처럼 들립니다. 반면에 소비자가 많고 생산자가 한 명인 경우 차단 동작이 필요하지 않을 수 있으며 소비자가 대기열이 비어 있는지 확인하고 대기열이 비어 있는지 계속 확인하게되어 기쁠 수도 있습니다.


67
대답은 오도의 소지가 있습니다. LinkedBlockingQueue와 ConcurrentLinkedQueue는 모두 큐의 헤드를 제거하거나 널 (블록 안함)을 리턴하는 "poll ()"메소드와 큐의 끝에 삽입하고 차단하지 않는 "offer (E e)"메소드를 가지고 있습니다. 차이점은 비 차단 작업 외에도 LinkedBlockingQueue 만 차단 작업 수행한다는 점입니다.이 권한에 따라 LinkedBlockingQueue가 실제로 일부 잠금을 보유한 가격을 지불하면됩니다. 다른 대답은 이것을 설명합니다.
Nakedible

123

ConcurrentLinkedQueue 는 잠금이 수행되지 않음을 의미합니다 (즉, 동기화 (this) 또는 Lock.lock 호출 이 없음 ). 그것은 사용 비교 및 스왑 - CAS 헤드 / 테일 노드가 여전히 시작했을 때와 동일합니다 있는지 확인하기 위해 수정하는 동안 작업을. 그렇다면 작업이 성공합니다. 헤드 / 테일 노드가 다른 경우 회전하여 다시 시도합니다.

LinkedBlockingQueue 는 수정하기 전에 잠금을 수행합니다. 따라서 오퍼 통화는 잠길 때까지 차단됩니다. 추가를 포기하기 전에 X 시간 동안 만 기꺼이 기다린다고 말하는데 TimeUnit을 사용하는 오퍼 오버로드를 사용할 수 있습니다 (일반적으로 메시지가 X 밀리 초 후에 오래된 메시지 유형 큐에 적합 함).

공정성은 잠금 구현이 스레드 순서를 유지함을 의미합니다. 스레드 A가 들어간 다음 스레드 B가 들어 오면 스레드 A가 먼저 잠금을 얻습니다. 공정성이 없으면 실제로 어떤 일이 발생하는지 정의되지 않습니다. 예정된 다음 스레드 일 가능성이 높습니다.

어느 것을 사용할지는 달려 있습니다. 생산자가 대기열에 올리는 데 시간이 걸리기 때문에 ConcurrentLinkedQueue 를 사용하는 경향이 있습니다 . 나는 같은 순간에 생산하는 생산자가 많지 않습니다. 그러나 여론 조사는 좋은 수면 상태로 들어 가지 않기 때문에 소비자 측이 더 복잡합니다. 직접 처리해야합니다.


1
그리고 어떤 조건에서 ArrayBlockingQueue가 LinkedBlockingQueue보다 낫습니까?
kolobok

@akapelko ArrayBlockingQueue를 사용하면보다 세부적인 순서를 지정할 수 있습니다.
IgorGanapolsky

2
그 의미는 무엇입니까- "돌아가서 다시 시도합니다." ?
레스터

9

질문 제목에 블로킹 대기열이 언급되어 있습니다. 그러나 차단 대기열 ConcurrentLinkedQueue아닙니다 .

BlockingQueue의는 ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue,와 SynchronousQueue.

이들 중 일부는 분명 목적에 적합하지 않은 ( DelayQueue, PriorityBlockingQueue, 및 SynchronousQueue). 후자는 이중 엔드 큐 (Deque 인터페이스를 구현 함)라는 점을 제외 LinkedBlockingQueue하고 LinkedBlockingDeque는 동일합니다.

ArrayBlockingQueue요소 수를 제한하려는 경우에만 유용 하기 때문에 에 충실합니다 LinkedBlockingQueue.


제목에서 차단 단어를 제거했습니다. 감사합니다. 내가 그것을 얻었는지 보자. 당신이 말한 것은 LinkedBlockingQueue가 다중 소비자에서 사용될 수 있고 동일한 객체에 대해 시나리오를 생성한다는 것을 의미합니까?
David Hofmann

1
ArrayBlockingQueue를 사용하면 스레드를보다 세분화 할 수 있다고 생각 했습니까? 따라서 장점.
IgorGanapolsky

4

ArrayBlockingQueue는 메모리 풋 프린트가 낮기 때문에 새로운 삽입마다 LinkedBlockingQueue $ Node 객체를 작성해야하는 LinkedBlockingQueue와 달리 요소 노드를 재사용 할 수 있습니다.


1
좋은 지적! LinkedBlockingQueue보다 ArrayBlockingQueue를 선호합니다

2
큐가 많은 시간 동안 비어 있지만 커질 수 있어야하는 경우 ArrayBlockingQueue메모리 발자국이 훨씬 나빠질 수 있습니다. 여전히 전체 메모리에 큰 배열이 할당되어 있습니다. 은 LinkedBlockingQueue비울 때 가까운 무시할 메모리 풋 프린트를해야합니다.
Krease

1
  1. SynchronousQueue(다른 질문 에서 발췌 )

SynchronousQueueLinkedBlockingQueue단일 요소를 허용하는 반면, 더 많은 핸드 오프 입니다. 차이가 있다고되는 put()(A)에 호출 SynchronousQueue대응있을 때까지 리턴되지 take()호출하지만과 LinkedBlockingQueue크기 1의 put()(비어있는 큐) 호출은 즉시 복귀된다. 그것은 BlockingQueue실제로 큐를 원하지 않을 때 (보류중인 데이터를 유지하고 싶지 않은) 구현입니다.

  1. LinkedBlockingQueue( LinkedList구현하지만 정확하게 JDK 구현 LinkedList은 정적 내부 클래스 노드를 사용하여 요소 간의 링크를 유지합니다)

LinkedBlockingQueue의 생성자

public LinkedBlockingQueue(int capacity) 
{
        if (capacity < = 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node< E >(null);   // Maintains a underlying linkedlist. ( Use when size is not known )
}

링크를 유지하는 데 사용되는 노드 클래스

static class Node<E> {
    E item;
    Node<E> next;
    Node(E x) { item = x; }
}

삼 . ArrayBlockingQueue (배열 구현)

ArrayBlockingQueue의 생성자

public ArrayBlockingQueue(int capacity, boolean fair) 
{
            if (capacity < = 0)
                throw new IllegalArgumentException();
            this.items = new Object[capacity]; // Maintains a underlying array
            lock = new ReentrantLock(fair);
            notEmpty = lock.newCondition();
            notFull =  lock.newCondition();
}

IMHO 가장 큰 생성자 ArrayBlockingQueue와의 차이점 LinkedBlockingQueue은 생성자 하나가 기본 데이터 구조 Array 및 기타 linkedList를 가지고 있다는 것 입니다.

ArrayBlockingQueue사용 단일 이중 잠금 상태 알고리즘LinkedBlockingQueue은 "두 로크 큐"알고리즘의 변형이며 2 잠금 2 조건 (takeLock, putLock)이


0

ConcurrentLinkedQueue에 잠금이없고 LinkedBlockingQueue가 없습니다. LinkedBlockingQueue.put () 또는 LinkedBlockingQueue.take ()를 호출 할 때마다 먼저 잠금을 획득해야합니다. 즉, LinkedBlockingQueue의 동시성이 좋지 않습니다. 성능이 마음에 들면 ConcurrentLinkedQueue + LockSupport를 시도하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.