Class NeoLock
Unlike synchronized blocks and ReentrantLock,
NeoLock does not park a thread while waiting. Waiters enqueue an
error-first callback; when the lock becomes free, the next waiter's callback fires with a fresh
Unlock token. This makes it suitable for callback chains where the acquire and the
release happen on different threads (or different virtual threads), which is the common case
with async I/O.
Why not just use synchronized?
Two reasons:
- Cross-thread release. A
synchronizedblock must be released by the same thread that acquired it. Inside a callback chain, the release usually happens inside an async completion handler running on a different worker.NeoLockmodels this directly: acquire returns a token via callback, release happens via that token. - VT-friendly under all JDKs. On JDK 21 with VTs,
synchronizedused to pin the carrier thread; JEP 491 (JDK 24) removes that constraint, butNeoLocknever had it.
The continuation that acquire(org.ores.async.Asyncc.IAsyncCallback<org.ores.async.Unlock, java.lang.Object>) fires is conventionally named c in code
examples (short for continuation). It receives the Unlock token instead of a
value.
Usage
NeoLock lock = new NeoLock("inventory");
lock.acquire((err, unlock) -> {
try {
// critical section. Safe to await async work inside here; the lock is held until
// unlock.releaseLock() is called.
mutateInventory(itemId);
} finally {
unlock.releaseLock();
}
});
For a critical section that awaits async work:
lock.acquire((err, unlock) -> {
fetchCurrentBalance(accountId, (e, balance) -> {
updateBalance(accountId, balance.subtract(amount), (e2, ok) -> {
unlock.releaseLock(); // released after the async update completes
reply.send(ok);
});
});
});
Calling unlock.releaseLock() a second time is a programming bug and logs a stack
trace to System.err but does not throw past the call site (which is usually a
finally block where re-throwing would mask the real error).
Concurrency contract (v0.2.x)
- The waiter queue and the
lockedflag are both guarded by theNeoLockinstance's monitor. (Earlier versions split those across two locks, allowing two concurrent acquirers in a tight race — fixed in v0.2.0.) - Waiter queue is an
ArrayDeque, popped from the head, pushed to the tail — FIFO ordering of pending acquirers. Unlockis a public sibling class (in v0.2.2+) soNeoLockis usable from any package.
-
Constructor Summary
Constructors -
Method Summary
-
Constructor Details
-
NeoLock
-
-
Method Details
-
getNamespace
-
makeUnlock
-
acquire
-