将Java读锁升级为写锁,用于Map中的缓存
我在下面的代码中遇到了死锁情况:
private static final ReadWriteLock opClassesLock = new ReentrantReadWriteLock();
private static final Map<Class<?>, ServiceClass> opClasses = new WeakHashMap<Class<?>, ServiceClass>();
public static ServiceClass get(Class<?> myClass) {
opClassesLock.readLock().lock();
try {
ServiceClass op = opClasses.get(myClass);
if (op == null) {
opClassesLock.writeLock().lock(); // deadlock here
try {
op = new ServiceClass(myClass);
opClasses.put(myClass, op);
} finally {
opClassesLock.writeLock().unlock();
}
}
return op;
} finally {
opClassesLock.readLock().unlock();
}
}
如果我检查了 ReentrantReadWriteLock
的文档,我就可以预测到这一点:
可重入还允许降级 从写锁到读锁,通过 获取写锁,然后 读锁,然后释放写锁 锁。 但是,从读取升级 锁不是写锁 可能。
除了使用单个锁而不是读/写锁(不允许并发读取)之外,还有其他方法可以解决此类问题吗?
I have a deadlock situation in the code below:
private static final ReadWriteLock opClassesLock = new ReentrantReadWriteLock();
private static final Map<Class<?>, ServiceClass> opClasses = new WeakHashMap<Class<?>, ServiceClass>();
public static ServiceClass get(Class<?> myClass) {
opClassesLock.readLock().lock();
try {
ServiceClass op = opClasses.get(myClass);
if (op == null) {
opClassesLock.writeLock().lock(); // deadlock here
try {
op = new ServiceClass(myClass);
opClasses.put(myClass, op);
} finally {
opClassesLock.writeLock().unlock();
}
}
return op;
} finally {
opClassesLock.readLock().unlock();
}
}
Had I checked the documentation for ReentrantReadWriteLock
, I could have predicted this:
Reentrancy also allows downgrading
from the write lock to a read lock, by
acquiring the write lock, then the
read lock and then releasing the write
lock. However, upgrading from a read
lock to the write lock is not
possible.
Besides just using a single lock instead of a read/write-lock (which won't allow concurrent reads), is there any other way to solve this kind of problem?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
使用经过充分测试的解决方案,例如
Guava 中的解决方案。您甚至可以做类似的事情
来解决您的整个问题,并提供很多调整缓存的可能性。
死锁的原因是在两个线程都持有读锁的情况下获取写锁。与降级锁不同,升级可能会阻塞。您需要先释放读锁。
当您获得写锁时,您应该测试另一个线程是否尚未完成该工作。
Use a well-tested solution like
from Guava. You can even do things like
which should solve your whole problem and offers a lot of possibilities to tune the caching.
The reason for the deadlock is acquiring the write lock while both thread are holding the read lock. Unlike downgrading the lock, upgrading may block. You'd need to release the read lock first.
When you get the write lock, you should test if another thread didn't do the work yet.
我现在意识到,上面的示例如果有效,无论如何都不会有效,因为这种情况可能会发生
所以有两个选项:
如果该条目不能创建两次(因为副作用)对整个方法使用单个锁。或者,释放读锁,获取写锁,并在计算之前再次检查该条目是否仍然丢失。
如果该条目可能被创建两次(如果它只是一个缓存),则在获取写锁之前释放读锁。
I realise now that the example above, if it worked, would not be valid anyway, because this could happen
So there are two options:
If the entry can't be made twice (because of side effects) use a single lock for the entire method. Or, release the read lock, acquire the write lock, and check again if the entry is still missing before calculating it.
If the entry may be made twice (if it's just a cache) release the read lock before getting the write lock.
我认为在获取写锁之前释放读锁可能会起作用。
但是在释放读锁后的同一时刻,其他线程可能会夺走该锁,从而导致前一个线程处于等待状态。
I think releasing the read lock before acquiring the write one may work.
But the at the exact moment after releasing the read lock, some other threads may take the lock away, which leads to the waiting of the previous thread.