Schlossfreie Lösung (?)
Ich hatte das gleiche Problem, wollte aber eine Lösung, bei der keine Sperren verwendet wurden.
Problem: Ich habe höchstens einen Thread aus einer Warteschlange. Mehrere Producer-Threads werden ständig in die Warteschlange eingefügt und müssen den Consumer benachrichtigen, wenn er wartet. Die Warteschlange ist sperrenfrei, sodass die Verwendung von Sperren für Benachrichtigungen zu unnötigen Blockierungen in Producer-Threads führt. Jeder Producer-Thread muss die Sperre erwerben, bevor er den wartenden Consumer benachrichtigen kann. Ich glaube , ich kam mit einer Lock-freier Lösung mit LockSupport
und AtomicReferenceFieldUpdater
. Wenn im JDK eine sperrfreie Barriere vorhanden ist, konnte ich sie nicht finden. Beide CyclicBarrier
und CoundDownLatch
verwenden intern Sperren von dem, was ich finden konnte.
Dies ist mein leicht abgekürzter Code. Mit diesem Code kann nur ein Thread gleichzeitig warten. Es könnte geändert werden, um mehrere Kellner / Verbraucher zuzulassen, indem eine Art atomare Sammlung zum Speichern mehrerer Eigentümer verwendet wird (a ConcurrentMap
funktioniert möglicherweise).
Ich habe diesen Code verwendet und es scheint zu funktionieren. Ich habe es nicht ausgiebig getestet. Ich empfehle Ihnen, die Dokumentation LockSupport
vor der Verwendung zu lesen .
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);
}
}
Um ein vages Beispiel für die Verwendung zu geben, werde ich das Beispiel von James Large übernehmen:
SignalBarrier barrier = new SignalBarrier();
Verbraucher-Thread (Singular, nicht Plural! ):
try {
while(!conditionIsTrue()) {
barrier.await();
}
doSomethingThatRequiresConditionToBeTrue();
} catch (InterruptedException e) {
handleInterruption();
}
Produzenten-Thread (s):
doSomethingThatMakesConditionTrue();
barrier.signal();