A lightweight Java library for acquiring multiple intrinsic locks (synchronized) without deadlocks.
The JVM’s synchronized keyword is simple and efficient for a single monitor, but taking more than one lock is easy to get wrong. Classic deadlock looks like this:
// Thread 1
synchronized (lockA) {
synchronized (lockB) { /* ... */ }
}
// Thread 2
synchronized (lockB) {
synchronized (lockA) { /* ... */ } // opposite order → deadlock
}If threads acquire the same locks in different orders, they can wait on each other forever. Fixing that by hand means agreeing on a global lock order everywhere in the codebase—and never violating it.
multi-synchronized does that ordering for you. It sorts the lock objects by System.identityHashCode and always acquires them in that order, so every thread in the JVM follows the same sequence.
- Deadlock-free multi-lock acquisition via a consistent global order
- Intrinsic locks only — uses nested
synchronized, noReentrantLockrequired - Fluent API —
MultiSynchronized.lock(...).run(...)or.call(...) - Reusable instances — build once, run many times
- Validation — rejects empty lock lists and duplicate lock objects
- Java 17+, virtual-thread friendly (same pinning caveats as any
synchronizeduse)
- Java 17 or newer
- No runtime dependencies
Maven (coordinates for Maven Central / local install):
<dependency>
<groupId>io.github.gorokhovskiy</groupId>
<artifactId>multi-synchronized</artifactId>
<version>1.0.0</version>
</dependency>Object accountLock = new Object();
Object ledgerLock = new Object();
MultiSynchronized.lock(accountLock, ledgerLock).run(() -> {
// Both locks are held here, always acquired in a safe order
transfer(from, to, amount);
});Order of arguments does not matter—lock(B, A) and lock(A, B) acquire the same way.
int balance = MultiSynchronized.lock(accountLock, ledgerLock).call(() ->
account.getBalance()
);MultiSynchronized sync = new MultiSynchronized(lockA, lockB, lockC);
sync.run(() -> doWork());
String result = sync.call(() -> "done");MultiSynchronized shared = MultiSynchronized.lock(lockA, lockB);
shared.run(() -> stepOne());
shared.run(() -> stepTwo());Useful when many threads share the same set of monitors.
MultiSynchronized.lock(); // no locks
MultiSynchronized.lock(lockA, lockA); // duplicate lock object- Copy the lock array and sort it by
System.identityHashCode(Object). - Reject duplicates (same object identity).
- Acquire locks recursively with nested
synchronizedblocks in sorted order. - Run your
Runnable/Supplieronce all locks are held; unlock in reverse as the stack unwinds.
Because the order is derived from object identity, it is consistent for all threads without a central registry.
mvn test
mvn packageApache License 2.0