비차단 대기열

연결된 노드에서 스레드 안전하고 가장 중요한 비차단 Queue 구현.

ConcurrentLinkedQueue<E> - 가비지 수집기와 함께 작동하도록 조정된 대기 없는 알고리즘을 사용합니다. 이 알고리즘은 CAS를 기반으로 구축되었기 때문에 매우 효율적이고 매우 빠릅니다. size() 메서드는오랫동안 실행될 수 있으므로 항상 끌어오지 않는 것이 가장 좋습니다.

ConcurrentLinkedDeque<E> - Deque는 이중 종료 대기열을 나타냅니다. 이는 양쪽에서 데이터를 추가하고 가져올 수 있음을 의미합니다. 따라서 이 클래스는 FIFO(First In First Out) 및 LIFO(Last In First Out)의 두 가지 작동 모드를 모두 지원합니다.

실제로 LIFO가 절대적으로 필요한 경우 ConcurrentLinkedDeque를 사용해야 합니다. 노드의 양방향성으로 인해 이 클래스는 ConcurrentLinkedQueue 에 비해 성능이 절반으로 떨어지기 때문입니다 .

import java.util.concurrent.ConcurrentLinkedQueue;

public class  ConcurrentLinkedQueueExample {
   public static void main(String[] args) {
       ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();

       Thread producer = new Thread(new Producer(queue));
       Thread consumer = new Thread(new Consumer(queue));

       producer.start();
       consumer.start();
   }
}

class Producer implements Runnable {

   ConcurrentLinkedQueue<String> queue;
   Producer(ConcurrentLinkedQueue<String> queue){
       this.queue = queue;
   }
   public void run() {
       System.out.println("Class for adding items to the queue");
       try {
           for (int i = 1; i < 5; i++) {
               queue.add("Item #" + i);
               System.out.println("Added: Item #" + i);
               Thread.sleep(300);
           }
       } catch (InterruptedException ex) {
           ex.printStackTrace();
           Thread.currentThread().interrupt();
       }
   }
}

class Consumer implements Runnable {

   ConcurrentLinkedQueue<String> queue;
   Consumer(ConcurrentLinkedQueue<String> queue){
       this.queue = queue;
   }

   public void run() {
       String str;
       System.out.println("Class for getting items from the queue");
       for (int x = 0; x < 5; x++) {
           while ((str = queue.poll()) != null) {
               System.out.println("Pulled out: " + str);
           }
           try {
               Thread.sleep(600);
           } catch (InterruptedException ex) {
               ex.printStackTrace();
               Thread.currentThread().interrupt();
           }
       }
   }
}

차단 대기열

BlockingQueue<E> 인터페이스 - 데이터가 많으면 ConcurrentLinkedQueue 로는 부족합니다.

스레드가 작업을 수행하지 못하면 OutOfMemmoryException 을 쉽게 얻을 수 있습니다 . 이러한 경우가 발생하지 않도록 대기열 및 조건부 잠금을 채우고 작업하는 다양한 방법이 있는 작업을 위한 BlockingQueue가 있습니다.

BlockingQueue는 null 요소를 인식하지 못하고이러한 요소를 추가하거나 가져오려고 할 때 NullPointerException을 발생시킵니다. poll 메서드는 제한 시간 내에 대기열에 요소가 배치되지 않은 경우 null 요소를 반환합니다.

BlockingQueue<E> 구현

각 BlockingQueue 구현을 자세히 살펴보겠습니다 .

ArrayBlockingQueue<E>는 클래식 링 버퍼에 구축된 차단 대기열 클래스입니다. 여기에서 자물쇠의 "정직성"을 관리할 기회가 있습니다. fair=false(기본값)이면 스레드 순서가 보장되지 않습니다.

DelayQueue<E extends Delayed>는 Delayed 인터페이스 의 getDelay 메서드 를 통해 각 요소에 정의된 특정 지연 이후에만 대기열에서 요소를 가져올 수 있도록 하는 클래스입니다.

LinkedBlockingQueue<E> 는 "2개의 잠금 대기열" 알고리즘으로 구현된 연결된 노드의 차단 대기열입니다. 첫 번째 잠금은 추가용이고 두 번째 잠금은 대기열에서 요소를 가져오기 위한 것입니다. 잠금으로 인해 ArrayBlockingQueue 에 비해이 클래스는 성능이 높지만 더 많은 메모리가 필요합니다. 대기열 크기는 생성자를 통해 설정되며 기본적으로 Integer.MAX_VALUE와 같습니다.

PriorityBlockingQueue<E>는 PriorityQueue 에 대한 다중 스레드 래퍼입니다. Comparator는 요소가 추가되는 논리를 담당합니다. 가장 작은 요소가 먼저 나옵니다.

SynchronousQueue<E> - 대기열은 FIFO(선입선출) 원칙에 따라 작동합니다. 각 삽입 작업은 "소비자" 스레드가 대기열에서 요소를 가져올 때까지 "생산자" 스레드를 차단하고 그 반대의 경우도 마찬가지입니다. "소비자"는 "생산자"가 요소를 삽입할 때까지 대기합니다.

BlockingDeque<E>는 양방향 차단 대기열에 대한 추가 메서드를 설명하는 인터페이스입니다. 대기열의 양쪽에서 데이터를 삽입하고 꺼낼 수 있습니다.

LinkedBlockingDeque<E>는 하나의 잠금이 있는 간단한 양방향 목록으로 구현된 연결된 노드의 양방향 차단 대기열입니다. 대기열 크기는 생성자를 통해 설정되며 기본적으로 Integer.MAX_VALUE와 같습니다.

TransferQueue<E> - 요소가 대기열에 추가될 때다른 소비자 스레드가 대기열에서 요소를 가져올 때까지 삽입하는 생산자 스레드를 차단할 수 있다는 점에서 인터페이스가 흥미롭습니다. 특정 시간 제한에 대한 확인을 추가하거나 대기 중인 Consumer 에 대한 확인을 설정할 수도 있습니다. 결과적으로 비동기 및 동기 메시지를 지원하는 데이터 전송 메커니즘을 얻습니다.

LinkedTransferQueue<E>는 Dual Queues with Slack 알고리즘을 기반으로 하는 TransferQueue의 구현입니다유휴 상태일 때 CAS(위 참조) 및 스레드 파킹을 많이 사용합니다.