Skip to content

Instantly share code, notes, and snippets.

@adamw
Created October 24, 2023 08:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adamw/bc23d9b9badfacfce832780ce80dea6b to your computer and use it in GitHub Desktop.
Save adamw/bc23d9b9badfacfce832780ce80dea6b to your computer and use it in GitHub Desktop.
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class Rendezvous {
private final boolean yieldOnFirstIteration;
private final AtomicReference<ThreadAndCell> waiting = new AtomicReference<>();
public Rendezvous(boolean yieldOnFirstIteration) {
this.yieldOnFirstIteration = yieldOnFirstIteration;
}
public void test() throws Exception {
long start = System.currentTimeMillis();
final int max = 10_000_000;
Thread t1 = Thread.ofVirtual().start(() -> {
Thread ourThread = Thread.currentThread();
for (int i = 0; i <= max; i++) {
AtomicReference<Integer> ourCell = new AtomicReference<>(i);
if (waiting.compareAndSet(null, new ThreadAndCell(ourThread, ourCell))) {
// CAS was successful, we are the first thread: parking and waiting for the data to be consumed
boolean tryYield = yieldOnFirstIteration;
while (ourCell.get() != -1) {
if (tryYield) {
Thread.yield();
tryYield = false;
} else {
LockSupport.park();
}
}
} else {
// CAS was unsuccessful, there is already a thread waiting for us: clearing `waiting` for the
// next iteration, sending the data using the provided cell and unparking the other thread
ThreadAndCell other = waiting.get();
waiting.set(null);
other.cell.set(i);
LockSupport.unpark(other.thread);
}
}
});
Thread t2 = Thread.ofVirtual().start(() -> {
long acc = 0L;
Thread ourThread = Thread.currentThread();
for (int i = 0; i <= max; i++) {
AtomicReference<Integer> ourCell = new AtomicReference<>(-1); // -1 -> no data provided yet
if (waiting.compareAndSet(null, new ThreadAndCell(ourThread, ourCell))) {
// CAS was successful, we are the first thread: parking and waiting for the data to be provided
boolean tryYield = yieldOnFirstIteration;
while (ourCell.get() == -1) {
if (tryYield) {
Thread.yield();
tryYield = false;
} else {
LockSupport.park();
}
}
acc += ourCell.get();
} else {
// CAS was unsuccessful, there is already a thread waiting for us: clearing `waiting` for the
// next iteration, consuming the data and unparking the other thread
ThreadAndCell other = waiting.get();
waiting.set(null);
acc += other.cell.get();
other.cell.set(-1);
LockSupport.unpark(other.thread);
}
}
assert acc == sumUpTo(max);
});
t1.join();
t2.join();
long end = System.currentTimeMillis();
System.out.println("Took (yield=" + yieldOnFirstIteration + "): " + (end - start) + " ms");
}
private long sumUpTo(int max) {
return ((long) max * (max + 1)) / 2;
}
private record ThreadAndCell(Thread thread, AtomicReference<Integer> cell) {}
public static void main(String[] args) throws Exception {
for (int i=0; i<3; i++) {
new Rendezvous(false).test();
new Rendezvous(true).test();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment