Striped locks, as popularized with Google Guava java library, allow the user to trade between required concurrency and memory footprint. However, I wonder if this is still true with Java 21 virtual threads? Also, are there are alternatives to Guava Striped<L> class (preferably java code depending on Java alone)? I do not want to add dependency to Guava just for this.
UPDATE: I am considering Guava "striped locks" replacement code like this - any opinions?
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class StripedLocks {
private final int stripes;
private final transient ReferenceQueue<LockHolder> queue;
private final transient ConcurrentHashMap<Integer, WeakReference<LockHolder>> locks;
public StripedLocks(int stripes) {
this.stripes = stripes;
queue = new ReferenceQueue<>();
locks = new ConcurrentHashMap<>();
}
public StripedLocks() {
this(4);
}
public final <T> T read(Object keyObject, Callable<T> readOperation) throws Exception {
return execute(getLock(keyObject).readLock(), readOperation);
}
public final <T> T write(Object keyObject, Callable<T> writeOperation) throws Exception {
return execute(getLock(keyObject).writeLock(), writeOperation);
}
private ReentrantReadWriteLock getLock(Object keyObject) {
Objects.requireNonNull(keyObject,"keyObject can't be null");
var key = keyObject.hashCode() % stripes;
return locks.compute(key, (_, currentReference) -> {
if(currentReference==null)
return new WeakReference<>(new LockHolder(key, new ReentrantReadWriteLock()), queue);
var lockHolder = currentReference.get();
if (lockHolder == null)
return new WeakReference<>(new LockHolder(key, new ReentrantReadWriteLock()), queue);
return currentReference;
}).get().lock();
}
private <T> T execute(Lock theLock, Callable<T> operation) throws Exception {
theLock.lock();
try {
return operation.call();
} finally {
theLock.unlock();
purgeStaleLocks();
}
}
@SuppressWarnings("unchecked")
void purgeStaleLocks() {
Reference<LockHolder> reference;
while ((reference = (Reference<LockHolder>) queue.poll()) != null) {
synchronized (queue) {
locks.remove(reference.get().key());
}
}
}
private record LockHolder(int key, ReentrantReadWriteLock lock) {}
}