ConcurrentLinkedQueue를 사용하는 방법?


95

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


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

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

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

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


mt 질문에 대한 답변에 감사드립니다. 내가하려는 것은 이것입니다 : 대기열에서 항목을 검색하는 하나의 메소드가있는 클래스 (Java)와 항목을 대기열에 추가하는 하나의 메소드가있는 다른 클래스가 있습니다. 목록에서 추가 및 검색된 항목은 내 클래스의 개체입니다.
Ricardo Felgueiras

2
질문을 편집하고 질문 자체에이 설명을 넣어야합니다.
Adam Jaskiewicz

답변:


156

아니요, 메서드를 동기화 할 필요가 없으며 메서드를 정의 할 필요가 없습니다. 이미 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.

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


응답 해 주셔서 감사합니다. 한 가지 더 질문 : remove 메서드에서이 작업을 수행해야합니까? while (queue.size () == 0) wait (); queue.poll ();
Ricardo Felgueiras

꽤 중요하기 때문에 제 답변에 대한 편집으로 대답하겠습니다.
Adam Jaskiewicz

다른 질문에서 혼란을 일으켰 기 때문에 구현하지 않는 Collection.synchronizedLista List를 반환합니다 Queue.
Tom Hawtin-tackline

@AdamJaskiewicz는 ConcurrentLinkedQueue생산자 소비자에게 좋은 아이디어를 사용 하고 있습니다.이 게시물을 참조하고 있습니다. stackoverflow.com/questions/1426754/…
rd22

37

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

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

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

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

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

queue.add(obj);

또는

queue.poll(obj);

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

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

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

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

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


1
내가 할 수있는 유일한 일반적인 용도는 대기열이 비어 있지 않을 때까지 차단하는 것입니다. BlockingQueue의 구현을 사용하면 더 나은 서비스를 제공하지 않겠습니까 (take ()는 원자적이고 소비 할 것이있을 때까지 차단됨)?
Adam Jaskiewicz

6
또한 조심해야합니다. 동기화 목록과 달리 ConcurrentLinkedQueue는 자체적으로 동기화되지 않으므로 코드에서 동기화 된 블록에있는 동안 생산자가 대기열에 제공 할 수 있습니다.
Adam Jaskiewicz

queue.isEmpty ()와 queue.poll ()을 결합하는 이유는 무엇입니까? 결과가 null인지 폴링하고 확인할 수 없습니까? (나의 이해는 빈 큐를 폴링 경우가 null을 반환한다는 것입니다)
AjahnCharles

마지막 코드 블록을 제외하고 코드의 모든 것에 동의합니다. ConcurrentLInkedQueue에서 동기화해도 다른 호출은 동기화되지 않으므로 아무것도 보장하지 않습니다. 따라서이 스레드를 동기화 했음에도 "isEmpty ()"와 "poll (...)"사이의 다른 스레드에서 "add (..)"가 발생할 수 있습니다
Klitos G.

6

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


6

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

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

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


대기열을 비울 때 매우 유용합니다. 감사.
DevilCode

2

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


1

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

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


ConcurrentLinkedQueue는 그렇지 않습니다. 여러 생산자와 소비자의 동시 액세스를 위해 처음부터 특별히 제작되었습니다. 간단한 래퍼 비트 애호가는 Collections.synchronized *에 의해 반환
아담 Jaskiewicz

그래, J2SE 5에 추가 된 깔끔한 동시성 많은 일들이 있었다
아담 Jaskiewicz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.