연결 리스트 기반으로 구현된, "블로킹(Blocking)" 기능이 있는 양방향 큐(Deque)
멀티스레드 환경에서 여러 스레드가 데이터를 안전하게 주고받을 때 쓰는 자료구조
"블로킹(Blocking)"이 핵심 무슨 뜻인가?
일반 큐 : 비어있는데 꺼내려고 하면 예외가 나거나 null이 반환
Deque<Integer> normalQueue = new ArrayDeque<>();
Integer value = novalQueue.pollFirst(); // 비어있으면 그냥 null 반환하고 끝
블로킹 큐 : 비어있으면 데이터가 들어올 때까지 그 스레드가 멈춰서(block) 기다린다.
LinkedBlockingDeque<Integer> blockingQueue = new LinkBlockingDeque<>();
Integer value = blockingQueue.tackFirst(); // 비어있으면 여기서 "정지" - 누가 넣어줄 때까지 대기
생산자(Producer) - 소비자(Consumer)패턴을 짤 때 진가를 발휘
생상자 - 소비자 패턴 예시
상황 : 한 스레드는 데이터를 계속 만들어서 큐에 넣고 (생산자), 다른 스레드는 큐에서 꺼내서 처리
import java.util.concurrent.LinkedBlockingDeque;
public class ProduceConsumerDemo {
public static void main(String[] args) {
LinkedBlockingDeque<Integer> queue = new LinkedBlockingDeque<>();
// 생산자 스레드 : 1초마다 숫자를 하나씩 만들어서 큐에 넣음
Thread produer = new Thread(() -> {
for(int i = 1; i <= 5; i++) {
try {
System.out.println("생산: " + i);
queue.putLast(i);
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
});
// 소비자 스레드 : 큐에서 꺼내서 처리. 없으면 자동으로 기다림
Thread consumer = new Thread(() -> {
for(int i = 1; i <= 5; i++) {
try {
int value = queue.takeFirst(); // 없으면 여기서 대기(block)
System.out.println("소비 : " + value);
} catch (InterruptedException e) {}
}
});
producer.start();
consumer.start();
}
}
실행 흐름
소비자 스레드가 takeFirst()를 호출했는데 큐가 비어있으면, 생산자가 뭔가 넣을 때까지 자동으로 대기. 개발자가 "루프 돌면서 계속 확인해봐" (polling) 같은 코드를 직접 짤 필요가 없어요. 이게 블로킹 큐의 핵심 장점
주요 메서드 (일반 메서드 vs 블로킹 메서드)
| 동작 | 일반(즉시 반환) | 블로킹(대기열) |
| 앞에 추가 | offerFirst(e) | putFirst(e) |
| 뒤에 추가 | offerLast(e) | putLast(e) |
| 앞에서 꺼내기 | pollFirst() | takeFIrst() |
| 뒤에서 꺼내기 | pollLast() | takeLast() |
- offer / poll : 즉시 시도하고, 안 되면 false나 null 반환하고 끝남
- put / take : 될 때까지 기다림(블로킹)
ConcurrentLinkedDeque와의 차이
| ConcurrentLinkedDeque | LinkedBlockingDeque | |
| 데이터 없을 때 꺼내려 하면 | 즉시 null 반환 | 스레드가 대기(block) |
| 크기 제한 | 없음 | 생성 시 지정 가능(new LinkedBlockingDeque<>(100)) |
| 내부 방식 | lock-free (CAS 기반, 더 빠름) | 락(lock) + 대기 / 알림 방식 |
| 적합한 상황 | "있으면 처리, 없으면 넘어가기" | "생산사-소비자처럼 서로 기다려줘야 하는 상황" |
정리
- 일반 ArrayDeque : 싱글스레드, 빠름, 동시성 고려 안 함
- ConcurrentLinkedDeque : 멀티스레드, lock-free, 없으면 즉시 null
- LinkedBlockingDeque : 멀티스레드, 없으면 알아서 기다려주는 큐 -> 생산자-소비자 패턴에 최적
즉 LinkedBlockingDeque는 "스레드 간에 서로 신호를 주고 받으며 협업해야 하는" 상황 (예: 작업 큐, 스레드풀 내부 구현)에서 쓰는 도구