LinkedBlockingQueue 대 ConcurrentLinkedQueue


112

내 질문은 이전에 질문 한이 질문과 관련 있습니다. 생산자와 소비자 스레드 간의 통신을 위해 대기열을 사용하는 상황에서 사람들은 일반적으로 LinkedBlockingQueue또는 사용을 권장 ConcurrentLinkedQueue합니까?

하나를 사용하는 장점 / 단점은 무엇입니까?

API 관점에서 볼 수있는 주요 차이점은 a LinkedBlockingQueue를 선택적으로 제한 할 수 있다는 것입니다.

답변:


110

생산자 / 소비자 스레드의 ConcurrentLinkedQueue경우 이것이 합리적인 옵션이라고 확신하지 못합니다 BlockingQueue. 생산자 / 소비자 대기열 IMO의 기본 인터페이스 인을 구현하지 않습니다 . 을 (를) 호출 poll()하고 아무것도 찾지 못한 경우 잠시 기다린 다음 다시 폴링해야합니다. 새 항목이 들어 오면 지연되고 비어 있으면 비효율적으로 발생합니다 (불필요하게 잠에서 깨어남). .

BlockingQueue에 대한 문서에서 :

BlockingQueue 구현은 주로 생산자-소비자 대기열에 사용되도록 설계되었습니다.

나는 그것을하지 않습니다 알고 엄격하게 에만 블로킹 큐는 생산자 - 소비자 큐에 사용하지만, 심지어해야한다는 말을 ...


4
고마워 Jon-나는 그것을 눈치 채지 못했습니다. 그렇다면 ConcurrentLinkedQueue를 어디서 / 왜 사용합니까?
Adamski

27
많은 스레드에서 큐에 액세스해야하지만 "기다릴"필요가없는 경우.
Jon Skeet

2
A ConcurrentLinkedQueue는 스레드가 여러 큐를 확인하는 경우에도 유용합니다. 예를 들어, 다중 테넌트 서버에서. 격리 이유 때문에 단일 차단 대기열과 테넌트 판별자를 대신 사용하지 않는다고 가정합니다.
LateralFractal 2010 년

귀하의 경우는 우리가 제한 되지 않은 대기열 에서 제한 된 대기열을 사용 take()하고 put().NET보다 추가 리소스 (동기화 기간)를 소비하는 경우에만 참으로 유지됩니다 ConcurrentLinkedQueue . 생산자-소비자 시나리오에
amarnath harish

@Adamski IMO, ConcurrentLinkedQueue는 다중 스레드 환경에서 사용되는 링크 목록입니다. 이에 대한 가장 좋은 비유는 ConcurrentHashMap 및 HashMap입니다.
Nishit

69

이 질문은 더 나은 대답을 할 가치가 있습니다.

Java ConcurrentLinkedQueue비 블로킹 잠금없는 대기열에 대해 Maged M. Michael과 Michael L. Scott 의 유명한 알고리즘을 기반으로합니다 .

여기서 경합 리소스 (우리 대기열)의 용어로 "비 차단"이란 스레드 중단과 같이 플랫폼의 스케줄러가 수행하는 작업에 관계없이 해당 스레드가 단순히 너무 느리다면 다른 스레드가 동일한 리소스에 대해 경합하는 것을 의미합니다. 계속 진행할 수 있습니다. 예를 들어 잠금이 관련되면 잠금을 보유한 스레드가 중단 될 수 있으며 해당 잠금을 기다리는 모든 스레드가 차단됩니다. synchronizedJava 의 고유 잠금 ( 키워드)은 바이어스 잠금 과 같이 성능에 심각한 불이익을 줄 수 있습니다.관련되고 경합이 있거나 VM이 스핀 유예 기간 후 잠금을 "팽창"하고 경합 스레드를 차단하기로 결정한 후 ... 이것이 많은 상황에서 (낮음 / 중간 경합 시나리오) 비교 및 ​​수행 -원자 참조에 대한 집합은 훨씬 더 효율적일 수 있으며 이것이 바로 많은 비 차단 데이터 구조가 수행하는 작업입니다.

자바 ConcurrentLinkedQueue는 비차 단일뿐만 아니라 생산자가 소비자와 경쟁하지 않는 멋진 속성을 가지고 있습니다. 단일 생산자 / 단일 소비자 시나리오 (SPSC)에서 이것은 실제로 말할 경합이 없음을 의미합니다. 다중 생산자 / 단일 소비자 시나리오에서 소비자는 생산자와 경쟁하지 않습니다. 이 대기열은 여러 생산자가을 시도 할 때 경합이 offer()있지만 이는 정의상 동시성입니다. 기본적으로 범용이며 효율적인 비 차단 대기열입니다.

그것이 아닌 것에 관해서 BlockingQueue는 스레드가 대기열에서 대기하도록 차단하는 것은 동시 시스템을 설계하는 끔찍한 방법입니다. 하지마. ConcurrentLinkedQueue소비자 / 생산자 시나리오에서 를 사용하는 방법을 알아낼 수없는 경우 좋은 행위자 프레임 워크와 같은 더 높은 수준의 추상화로 전환하십시오.


8
마지막 단락에서 대기열에서 대기하는 것이 동시 시스템을 설계하는 끔찍한 방법이라고 말하는 이유는 무엇입니까? 태스크 큐에서 태스크를 먹는 10 개의 스레드가있는 스레드 그룹이있는 경우 태스크 큐에 10 개 미만의 태스크가있을 때 차단하는 것이 잘못된 것은 무엇입니까?
Pacerier

11
@AlexandruNedelcu 당신이 말하는 바로 그 행위자 프레임 워크가 BlockingQueue 의 쓰레드 풀을 자주 사용하는 "정말 끔찍한"과 같은 포괄적 인 진술을 할 수는 없습니다 . 반응성이 높은 시스템이 필요하고 역압을 처리하는 방법을 알고 있다면 (대기열 차단을 완화하는 것) 비 차단보다 분명히 우수합니다. 그러나 .. 종종 IO를 차단하고 대기열을 차단하면 특히 IO에 묶여 있고 정복 할 수없는 장기 실행 작업이있는 경우 비 차단을 수행 할 수 있습니다.
Adam Gent

1
@AdamGent-행위자 프레임 워크에는 차단 대기열을 기반으로하는 사서함 구현이 있지만 차단은 비동기 경계에서 작동하지 않으므로 데모에서만 작동하기 때문에 제 생각에는 버그입니다. 예를 들어 오버플로를 처리하는 Akka의 개념은 아직 출시되지 않은 버전 2.4까지 메시지를 삭제하는 대신 차단하는 것입니다. 즉, 차단 대기열이 우월 할 수있는 사용 사례가 있다고 생각하지 않습니다. 또한 합쳐서는 안되는 두 가지를 합치 게됩니다. I / O 차단에 대해 언급 한 적이 없습니다.
Alexandru Nedelcu 2015 년

1
@AlexandruNedelcu 내가 일반적으로 배압에 대해 동의하지만 위에서 아래로 "잠금없는"시스템을 아직 보지 못했습니다. Node.js, Erlang, Golang 등 기술 스택의 어딘가에서 차단 대기열 (잠금) 또는 CAS 회전 여부에 관계없이 일종의 대기 전략을 사용하며 경우에 따라 기존 잠금 전략이 더 빠릅니다. 일관성 때문에 잠금을 갖지 않는 것이 매우 어렵고 이것은 io 및 ~ Producer / Consumer 인 스케줄러를 차단하는 데 특히 중요합니다. ForkJoinPool은 단기 실행 작업에서 작동하며 여전히 CAS 회전 잠금이 있습니다.
Adam Gent

1
@AlexandruNedelcu 스케줄러 및 스레드 풀링에 필요한 패턴 인 생산자 / 소비자 패턴에 대해 ConcurrentLinkedQueue (제한된 btw가 아니므로 내 약한 역압 인수)를 사용할 수있는 방법을 보여줄 수 있는지 보여줄 수있을 것 같습니다. BlockingQueue는 절대 사용되어서는 안됩니다. (그리고 akka는 생산자 / 소비자이기 때문에 차례로 차단 / 대기 작업을하기 때문에 예약을 수행하는 다른 작업에 속이고 위임 할 수 없습니다).
Adam Gent 2015 년

33

LinkedBlockingQueue큐가 비어 있거나 가득 차고 각 소비자 / 생산자 스레드가 휴면 상태가 될 때 소비자 또는 생산자를 차단합니다. 그러나이 차단 기능에는 비용이 수반됩니다. 모든 풋 또는 테이크 작업은 생산자 또는 소비자 (많은 경우)간에 경합되는 잠금이므로 생산자 / 소비자가 많은 시나리오에서는 작업이 느려질 수 있습니다.

ConcurrentLinkedQueue풋 / 테이크 작업에서 잠금을 사용하지 않고 CAS 는 잠재적으로 많은 생산자 및 소비자 스레드와의 경합을 줄입니다. 그러나 "wait free"데이터 구조 ConcurrentLinkedQueue는 비어있을 때 차단되지 않습니다. 즉 , 소비자 스레드가 CPU를 소모하는 경우와 같이 소비자가 "바쁨 대기" take()로 반환 null값 을 처리해야합니다 .

따라서 어느 것이 "더 좋은지"는 소비자 스레드의 수, 소비 / 생산 속도 등에 따라 달라집니다. 각 시나리오에 대한 벤치 마크가 필요합니다.

ConcurrentLinkedQueue분명히 더 나은 한 가지 특별한 사용 사례 는 생산자가 처음으로 무언가를 생산하고 작업을 대기열에 배치하여 작업을 완료 하고 소비자가 소비를 시작한 후에야 대기열이 비어있을 때 완료된다는 것을 알고있는 경우입니다. (여기에 생산자-소비자 간에는 동시성이 없지만 생산자-생산자 및 소비자-소비자간에 만 동시성이 있습니다.)


여기에 한 가지 의심이 있습니다. 언급했듯이 소비자는 대기열이 비어있을 때 대기합니다. 기다리지 말라고 누가 알릴까요?
Brinal

@brindal 내가 아는 유일한 대기 방법은 루프입니다. 여기에 대한 답변에서별로 관심을 기울이지 않은 중요한 문제입니다. 데이터를 기다리는 루프를 실행하는 것만으로도 많은 프로세서 시간이 사용됩니다. 팬이 활기를 띠기 시작하면 알 수 있습니다. 유일한 해결책은 루프에 잠을자는 것입니다. 따라서 데이터 흐름이 일관되지 않은 시스템의 문제입니다. 아마도 나는 AlexandruNedelcu의 대답을 오해하지만 운영 체제 자체는 동시 시스템이므로 비 차단 이벤트 루프로 가득 차 있다면 매우 비효율적입니다.
orodbhen

괜찮습니다.하지만 unbounded blockingqueue사용된다면 CAS 기반 동시 사용 보다 더 좋을 것입니다ConcurrentLinkedQueue
amarnath harish

@orodbhen 잠을 자도 낭비가 없어지지는 않습니다. OS는 스레드를 절전에서 해제하고 일정을 잡고 실행하기 위해 많은 작업을 수행해야합니다. 메시지를 아직 사용할 수없는 경우 OS에서 수행 한 작업이 낭비됩니다. 생산자-소비자 문제를 위해 특별히 설계되었으므로 BlockingQueue를 사용하는 것이 좋습니다.
Nishit

사실 저는 "소비 / 생산 비율"부분에 매우 관심이 있는데, 비율이 높으면 어느 것이 더 좋을까요?
workplaylifecycle


0

대기열이 확장 불가능하고 하나의 생산자 / 소비자 스레드 만 포함하는 경우. 잠금없는 대기열을 사용할 수 있습니다 (데이터 액세스를 잠글 필요가 없음).

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