Беззамкове рішення (?)
У мене була та сама проблема, але я хотів рішення, яке не використовувало замки.
Проблема: у мене є щонайбільше один потік, який споживає з черги. Кілька потоків виробників постійно вставляють у чергу і повинні повідомити споживача, якщо він чекає. Черга не блокується, тому використання блокування для сповіщення спричиняє непотрібне блокування у потоках виробника. Кожен потік виробника повинен отримати замок, перш ніж він зможе повідомити споживача, який чекає. Я вважаю, що придумав рішення, що не містить замків, за допомогою LockSupport
і AtomicReferenceFieldUpdater
. Якщо в JDK існує перешкода без замків, я не міг її знайти. І те, CyclicBarrier
і CoundDownLatch
інше використовуйте замки від того, що я міг знайти.
Це мій дещо скорочений код. Щоб бути зрозумілим, цей код дозволить зачекати лише одному потоку. Він може бути модифікований, щоб дозволити численним офіціантам / споживачам, використовуючи певний тип атомної колекції для зберігання кількох власників ( ConcurrentMap
може працювати).
Я використав цей код, і він, здається, працює. Я його широко не тестував. Я пропоную вам прочитати документацію LockSupport
перед використанням.
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.LockSupport;
public class SignalBarrier {
@SuppressWarnings("unused")
private volatile Thread _owner;
private static final AtomicReferenceFieldUpdater<SignalBarrier, Thread> ownerAccess =
AtomicReferenceFieldUpdater.newUpdater(SignalBarrier.class, Thread.class, "_owner");
public SignalBarrier() {
_owner = null;
}
public void signal() {
Thread t = ownerAccess.getAndSet(this, null);
if (t != null) {
LockSupport.unpark(t);
}
}
public void await() throws InterruptedException {
Thread t = Thread.currentThread();
if (!ownerAccess.compareAndSet(this, null, t)) {
throw new IllegalStateException("A second thread tried to acquire a signal barrier that is already owned.");
}
LockSupport.park(this);
ownerAccess.compareAndSet(this, t, null);
if (t.isInterrupted())
throw new InterruptedException();
}
public long awaitNanos(long timeout) throws InterruptedException {
if (timeout <= 0)
return 0;
Thread t = Thread.currentThread();
if (!ownerAccess.compareAndSet(this, null, t)) {
throw new IllegalStateException("A second thread tried to acquire a signal barrier is already owned.");
}
long start = System.nanoTime();
LockSupport.parkNanos(this, timeout);
ownerAccess.compareAndSet(this, t, null);
long stop = System.nanoTime();
if (t.isInterrupted())
throw new InterruptedException();
return Math.max(timeout - stop + start, 0L);
}
}
Щоб дати неясний приклад використання, я скористаюся прикладом Джеймса Великого:
SignalBarrier barrier = new SignalBarrier();
Споживча нитка (однина, а не множина! ):
try {
while(!conditionIsTrue()) {
barrier.await();
}
doSomethingThatRequiresConditionToBeTrue();
} catch (InterruptedException e) {
handleInterruption();
}
Виробничі нитки:
doSomethingThatMakesConditionTrue();
barrier.signal();