[java] ConcurrentLinkedQueue를 사용하는 방법?

ConcurrentLinkedQueueJava에서를 어떻게 사용 합니까?
이것을 사용 LinkedQueue하면 대기열의 동시성에 대해 걱정해야합니까? 아니면 두 가지 메서드를 정의해야합니까 (하나는 목록에서 요소를 검색하고 다른 하나는 목록에 요소를 추가하는 것)?
참고 : 분명히이 두 가지 방법은 동기화되어야합니다. 권리?


편집 : 내가하려는 것은 이것입니다 : 대기열에서 항목을 검색하는 한 가지 방법이있는 클래스 (Java)와 대기열에 항목을 추가하는 한 가지 방법이있는 다른 클래스가 있습니다. 목록에서 추가 및 검색된 항목은 내 클래스의 개체입니다.

한 가지 더 질문 : remove 메서드에서이 작업을 수행해야합니까?

while (queue.size() == 0){
  wait();
  queue.poll();
}

소비자와 생산자는 한 명뿐입니다.



답변

아니요, 메서드를 동기화 할 필요가 없으며 메서드를 정의 할 필요가 없습니다. 이미 ConcurrentLinkedQueue에 있으므로 사용하면됩니다. ConcurrentLinkedQueue는 내부적으로 필요한 모든 잠금 및 기타 작업을 수행합니다. 생산자는 대기열에 데이터를 추가하고 소비자는 데이터를 폴링합니다.

먼저 대기열을 만듭니다.

Queue<YourObject> queue = new ConcurrentLinkedQueue<YourObject>();

이제 생산자 / 소비자 객체를 생성 할 때마다 대기열을 전달하여 객체를 넣을 어딘가에 있도록합니다 (대신 setter를 사용할 수 있지만 생성자에서 이런 종류의 작업을 수행하는 것을 선호합니다).

YourProducer producer = new YourProducer(queue);

과:

YourConsumer consumer = new YourConsumer(queue);

생산자에 항목을 추가하십시오.

queue.offer(myObject);

소비자에서 물건을 꺼내십시오 (큐가 비어 있으면 poll ()이 null을 반환하므로 확인하십시오).

YourObject myObject = queue.poll();

자세한 정보 는 Javadoc을 참조하십시오 .

편집하다:

큐가 비어 있지 않을 때까지 기다리는 것을 차단해야하는 경우 LinkedBlockingQueue 를 사용하는 것이 좋습니다. 를 사용하고 take () 메서드 . 그러나 LinkedBlockingQueue에는 최대 용량 (기본값은 Integer.MAX_VALUE, 20 억 이상)이 있으므로 상황에 따라 적절하지 않을 수도 있습니다.

하나의 스레드 만 대기열에 항목을 넣고 다른 스레드가 대기열에서 항목을 가져 오는 경우 ConcurrentLinkedQueue는 아마도 과잉 일 것입니다. 동시에 수백 또는 수천 개의 스레드가 대기열에 액세스 할 수있는 경우에 더 적합합니다. 다음을 사용하여 요구 사항을 충족 할 수 있습니다.

Queue<YourObject> queue = Collections.synchronizedList(new LinkedList<YourObject>());

이것의 장점은 인스턴스 (대기열)에서 잠기므로 복합 작업의 원 자성을 보장하기 위해 대기열에서 동기화 할 수 있다는 것입니다 (Jared가 설명 함). 모든 작업이 인스턴스에 대한 잠금없이 수행되므로 (java.util.concurrent.atomic 변수 사용) ConcurrentLinkedQueue로는이를 수행 할 수 없습니다. 큐가 비어있는 동안 차단하려면이 작업을 수행 할 필요가 없습니다. poll ()은 큐가 비어있는 동안 단순히 null을 반환하고 poll ()은 원자 적이기 때문입니다. poll ()이 null을 반환하는지 확인하십시오. 그렇다면 wait () 다음 다시 시도하십시오. 잠글 필요가 없습니다.

드디어:

솔직히 LinkedBlockingQueue를 사용합니다. 응용 프로그램에는 여전히 과잉이지만 잘 작동 할 가능성이 있습니다. 성능이 충분하지 않으면 (PROFILE!), 언제든지 다른 것을 시도 할 수 있으며 동기화 된 항목을 다룰 필요가 없음을 의미합니다.

BlockingQueue<YourObject> queue = new LinkedBlockingQueue<YourObject>();

queue.put(myObject); // Blocks until queue isn't full.

YourObject myObject = queue.take(); // Blocks until queue isn't empty.

다른 모든 것은 동일합니다. 넣어 아마 당신은 가능성이 대기열에 억 객체를 넣어하지 않기 때문에, 차단하지 않습니다.


답변

이것은 대체로 다른 질문과 중복 됩니다.

이 질문과 관련된 답변 섹션은 다음과 같습니다.

java.util.ConcurrentLinkedQueue를 사용하는 경우 자체 동기화를 수행해야합니까?

동시 컬렉션에 대한 원자 적 작업이 동기화됩니다. 즉, 대기열에 대한 각 개별 호출은 사용자의 조치없이 스레드로부터 안전합니다. 무엇입니다 하지 스레드 안전 당신이 아닌 원자 인 컬렉션 수행하는 모든 작업입니다 보장.

예를 들어, 이것은 사용자의 조치없이 스레드 세이프입니다.

queue.add(obj);

또는

queue.poll(obj);

하나; 큐에 대한 비원 자적 호출은 자동으로 스레드로부터 안전하지 않습니다. 예를 들어 다음 작업은 자동으로 스레드로부터 안전 하지 않습니다 .

if(!queue.isEmpty()) {
   queue.poll(obj);
}

마지막 것은 스레드 세이프가 아닙니다. isEmpty가 호출되고 폴링이 호출되는 시간 사이에 다른 스레드가 큐에서 항목을 추가하거나 제거 할 가능성이 매우 높기 때문입니다. 이를 수행하는 스레드 세이프 방법은 다음과 같습니다.

synchronized(queue) {
    if(!queue.isEmpty()) {
       queue.poll(obj);
    }
}

다시 … 대기열에 대한 원자 적 호출은 자동으로 스레드로부터 안전합니다. 비 원자 호출은 그렇지 않습니다.


답변

poll 을 사용 하여 첫 번째 요소를 가져오고 add 를 사용하여 새 마지막 요소를 추가합니다. 그게 전부입니다. 동기화 나 다른 것은 없습니다.


답변

이것은 아마도 큐의 모든 것을 소비하려고 할 때 스레드 안전성과 “예쁘다”라는 측면에서 찾고있는 것일 것입니다.

for (YourObject obj = queue.poll(); obj != null; obj = queue.poll()) {
}

이렇게하면 큐가 비어있을 때 종료되고 비어 있지 않은 한 계속해서 개체를 팝합니다.


답변

ConcurentLinkedQueue는 매우 효율적인 대기 / 잠금없는 구현입니다 (참조는 javadoc 참조). 따라서 동기화 할 필요가 없을뿐만 아니라 대기열이 아무것도 잠그지 않으므로 사실상 동기화되지 않은 것만 큼 빠릅니다 (스레드가 아님). 안전) 하나.


답변

비 동시 컬렉션처럼 사용하십시오. Concurrent [Collection] 클래스는 액세스 동기화에 대해 생각할 필요가 없도록 일반 컬렉션을 래핑합니다.

편집 : ConcurrentLinkedList는 실제로 래퍼가 아니라 더 나은 동시 구현입니다. 어느 쪽이든 동기화에 대해 걱정할 필요가 없습니다.


답변