빠른 대답은 '예, 그들은 스레드로부터 안전합니다. 그러나 거기에 두지 마십시오. ...
작은 집 보관, BlockingQueue
은 인터페이스이며 스레드로부터 안전하지 않은 모든 구현은 문서화 된 계약을 위반하게됩니다. 포함 된 링크는 LinkedBlockingQueue
을 언급하고 있는데, 약간의 영리성이 있습니다.
link that you included은 재미있는 관찰을합니다. 예. LinkedBlockingQueue
내에 두 개의 잠금이 있습니다. 그러나 '단순한'구현이 파울을 범하는 에지 케이스가 실제로 처리된다는 것을 이해하지 못합니다. 따라서 테이크 및 푸시 메소드가 처음 예상했던 것보다 더 복잡합니다.
LinkedBlockingQueue
은 읽기 및 쓰기에서 동일한 잠금을 사용하지 않도록 최적화되어 있지만 충돌이 줄어들지 만 올바른 동작을 위해서는 비어 있지 않은 큐를 사용합니다. 큐가 그 안에 요소를 가지고 있으면 푸시와 팝 포인트는 같은 메모리 영역에 있지 않으므로 경합을 피할 수 있습니다. 그러나 대기열이 비어있는 경우 경합을 피할 수 없으므로이 일반적인 '가장자리'의 경우를 처리하는 데 추가 코드가 필요합니다. 이는 코드 복잡성과 성능/확장 성 간의 일반적인 상충 관계입니다.
다음 질문은 다음과 같습니다. LinkedBlockingQueue
은 대기열이 비어 있거나 비어 있지 않은 것을 어떻게 알 수 있습니까? 그러면 스레딩을 처리 할 수 있습니까? 대답은 두 개의 추가 동시 데이터 구조로 AtomicInteger
및 Condition
을 사용한다는 것입니다. AtomicInteger
은 큐의 길이가 0인지 여부와 큐가 아마도 원하는 상태에있을 때 신호가 대기 스레드에 알리기를 기다리는 데 사용되는 조건을 확인하는 데 사용됩니다. 이 추가 조정에는 오버 헤드가 있지만 측정시 동시 스레드 수를 늘리면이 기술의 오버 헤드가 단일 잠금을 사용하여 도입 된 경합보다 낮음을 알 수 있습니다.
아래 코드를 LinkedBlockingQueue
에서 복사하고 작동 방식을 설명하는 주석을 추가했습니다. 높은 수준에서 take()
은 먼저 다른 모든 호출을 take()
으로 잠근 다음 필요에 따라 put()
을 신호로 보냅니다. put()
도 비슷한 방식으로 작동합니다. 먼저 다른 모든 호출을 put()
으로 차단 한 다음 필요한 경우 take()
에 신호를 보냅니다. put()
방법에서
:
// putLock coordinates the calls to put() only; further coordination
// between put() and take() follows below
putLock.lockInterruptibly();
try {
// block while the queue is full; count is shared between put() and take()
// and is safely visible between cores but prone to change between calls
// a while loop is used because state can change between signals, which is
// why signals get rechecked and resent.. read on to see more of that
while (count.get() == capacity) {
notFull.await();
}
// we know that the queue is not full so add
enqueue(e);
c = count.getAndIncrement();
// if the queue is not full, send a signal to wake up
// any thread that is possibly waiting for the queue to be a little
// emptier -- note that this is logically part of 'take()' but it
// has to be here because take() blocks itself
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
대답은 조금 이상한 것을
takeLock.lockInterruptibly();
try {
// wait for the queue to stop being empty
while (count.get() == 0) {
notEmpty.await();
}
// remove element
x = dequeue();
// decrement shared count
c = count.getAndDecrement();
// send signal that the queue is not empty
// note that this is logically part of put(), but
// for thread coordination reasons is here
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
알았어, 알았어. 만약 내가'BlockingQueue를 할 때처럼 들린다. killAllMailbox = new LinkedBlockingQueue ();'나는 안전하다? –
Josh
@ Josh 예, 당신은 분명히 있습니다. 이 클래스는 많은 사람들이 잘 테스트 해 왔습니다. 드문 경우이지만, 문제가 발생했다면 JDK 버그 일 것이고 Java 유지 보수 담당자는 문제를 해결할 것입니다. 하지만 안심할 수 있습니다. 몇 년 동안 잘 테스트되고 사용되었습니다. –
그런 세부 사항에 들어가 주셔서 감사합니다! – Josh