[java] LinkedBlockingQueue 대 ConcurrentLinkedQueue

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

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

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



답변

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

BlockingQueue에 대한 문서에서 :

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

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


답변

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

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

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

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

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


답변

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

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

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

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


답변

(잘 확장되지 않는) 또 다른 솔루션은 랑데뷰 채널입니다. java.util.concurrent SynchronousQueue


답변

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


답변