Locking Problem with Lazy Striped ReadWriteLocks
See original GitHub issueWe’re seeing a behaviour of a Striped.lazyReadWriteLock that seem to break the documented behaviour that for the same key the same lock will be returned. Is there something obvious we’re missing or misunderstood about the lazy Striped Locks? When using eager Locks, everything works as expected.
The following test reproduces the behaviour.
public class MyTest {
private final Striped<ReadWriteLock> stripedLock = Striped.lazyWeakReadWriteLock(64);
private final AtomicBoolean writeLocked = new AtomicBoolean(false);
private final Set<String> someSet = new HashSet<>();
private final Random random = new Random();
private final String key = "anyKey";
@Test
public void test() throws Exception {
final ExecutorService executorService = Executors.newCachedThreadPool();
final AtomicBoolean testFailed = new AtomicBoolean(false);
for (int i = 0; i < 1000000; i++) {
someSet.add("" + i);
}
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
try {
while (!testFailed.get()) {
if (random.nextBoolean()) {
writeLockedMethod();
} else {
readLockedMethod();
}
}
} catch (AssertionError e) {
e.printStackTrace();
testFailed.set(true);
}
}
});
}
while (!testFailed.get()) {
Thread.sleep(1000);
}
fail();
}
private void readLockedMethod() {
final Lock readLock = stripedLock.get(key).readLock();
readLock.lock();
assertFalse(writeLocked.get());
try {
someSet.contains("" + random.nextInt(1000000)); //Just do something.
} finally {
readLock.unlock();
}
}
private void writeLockedMethod() {
final Lock writeLock = stripedLock.get(key).writeLock();
writeLock.lock();
//Since the atomic boolean is set inside the write lock, for the same key, this should never be true.
assertFalse(Thread.currentThread().getName(),writeLocked.get());
writeLocked.getAndSet(true);
try {
someSet.contains("" + random.nextInt(1000000)); //Just do something.
} finally {
writeLocked.getAndSet(false);
writeLock.unlock();
}
}
}
Issue Analytics
- State:
- Created 7 years ago
- Reactions:4
- Comments:12 (6 by maintainers)
Top Results From Across the Web
java - Attempt to unlock read lock, not locked by current thread
Looks like there is a bug in Striped.lazyWeakReadWriteLock(). By changing the initialization from Striped.lazyWeakReadWriteLock to Striped.
Read more >Introduction to Lock Striping - Baeldung
Lock striping is a technique where the locking occurs on several buckets or stripes, meaning that accessing a bucket only locks that bucket...
Read more >Cache<Long, BlockingDeque<Peer>> combined with Striped ...
From a concurrency stand-point, I don't see any issue with the code: ... getPeers(long sessionId) { Lock lock = stripes.get(sessionId).
Read more >To Lock or Not to Lock. I never really enjoy solving… - Medium
This sounds like a text book problem for shared locks, right? ... SmallLazyStriped class uses a AtomicReferenceArray to store locks.
Read more >Striped (Guava: Google Core Libraries for Java 19.0 API)
Creates a Striped<ReadWriteLock> with lazily initialized, weakly referenced read-write locks. static Striped<Semaphore>, lazyWeakSemaphore(int stripes, int ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
As surprising as this behavior is, I think it’s…technically…spec-compliant. Took me a couple minutes to figure it out. That doesn’t mean it’s not unacceptably surprising, or fixable, though.
The issue isn’t the laziness, it’s the weak part. The lock is weakly referenced by the Striped; it says so in the “weak” part of the method name.
The issue is that those two methods of yours don’t hold on to the
ReadWriteLock
itself. They hold on to the read lock and to the write lock. Separately. And those don’t hold references back to theReadWriteLock
. So theReadWriteLock
is eligible for garbage collection in the middle of the method, and a new one gets generated the next time someone asks for it, and since it’s not the same lock they interoperate at the same time.That said: that’s an artifact of the Java implementation of
ReentrantReadWriteLock
, that the read and write locks don’t actually hold references back to the shared lock object they came from. And it makeslazyWeakReadWriteLock
almost useless. So I think the proper thing to do is, in fact, to return not aReentrantReadWriteLock
but a thin wrapper that makes sure the read and write locks hold references to theReadWriteLock
they came from.https://bugs.openjdk.java.net/browse/JDK-8189598