【问题标题】:Upgrade Java read lock to write lock for caching in Map将 Java 读锁升级为写锁以在 Map 中进行缓存
【发布时间】:2011-02-03 12:29:54
【问题描述】:

我在下面的代码中出现了死锁情况:

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 的文档,我可以预测到:

重入也允许降级 从写锁到读锁,通过 获取写锁,然后 读锁,然后释放写 锁。 但是,从读取升级 锁到写锁不是 可能。

除了只使用单个锁而不是读/写锁(不允许并发读取)之外,还有其他方法可以解决此类问题吗?

【问题讨论】:

    标签: java concurrency synchronization locking deadlock


    【解决方案1】:

    使用经过充分测试的解决方案,例如

    new MapMaker().weakKeys().makeMap();
    

    来自Guava。你甚至可以做类似的事情

    new MapMaker().weakKeys()
    .concurrencyLevel(16)
    .expireAfterAccess(5, TimeUnit.MINUTES) 
    .maximumSize(1000)
    .makeComputingMap(new Function<Class<?>, ServiceClass>() {
        @Override
        public ServiceClass apply(Class<?> myClass) {
            return new ServiceClass(myClass);
        }
    });
    

    这应该可以解决您的整个问题,并提供很多调整缓存的可能性。


    死锁的原因是在两个线程都持有读锁的情况下获得了写锁。与降级锁不同,升级可能会阻塞。你需要先释放读锁。


    当你获得写锁时,你应该测试是否有另一个线程还没有完成这项工作。

    【讨论】:

    • 是的,这正是我所需要的。我将使用它并查看源代码以了解其工作原理。
    【解决方案2】:

    我现在意识到上面的例子,如果它有效,无论如何都不会有效,因为这可能发生

    • 线程 1 发现映射中没有条目并获得写锁
    • 线程 2 看到映射中没有条目并获得写锁(但必须等待)
    • 线程 1 添加条目并释放写锁
    • 线程 2 现在添加条目,但它已经存在

    所以有两种选择:

    1. 如果无法输入两次(由于副作用),则对整个方法使用单个锁。或者,释放读锁,获取写锁,在计算之前再次检查条目是否仍然丢失。

    2. 如果条目可能被创建两次(如果它只是一个缓存),则在获得写锁之前释放读锁。

    【讨论】:

      【解决方案3】:

      我认为在获取写锁之前释放读锁可能会奏效。 但是在释放读锁的确切时刻,其他一些线程可能会拿走锁,这会导致前一个线程的等待。

      【讨论】:

        猜你喜欢
        • 2011-01-25
        • 2010-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多