java - 写时读锁

标签 java readwritelock reentrantreadwritelock

我需要帮助来理解以下代码:

private Predicate composedPredicate = null;

public boolean evaluate(Task taskData) {
        boolean isReadLock = false;
        try{
            rwl.readLock().lock();
            isReadLock = true;
            if (composedPredicate == null) {
                rwl.readLock().unlock();
                isReadLock = false;
                rwl.writeLock().lock();
                if (composedPredicate == null) {
                    //write to the "composedPredicate" object
                }
            }
        }finally {
            if (isReadLock) {
                rwl.readLock().unlock();
            }else{
                rwl.writeLock().unlock();
            }
        }
        return composedPredicate.test(taskData);
    }

如果我们在上面的代码中不使用读锁会发生什么? 喜欢:

public boolean evaluate(Task taskData) {
        //boolean isReadLock = false;
        try{
            //rwl.readLock().lock();
            //isReadLock = true;
            if (composedPredicate == null) {
                //rwl.readLock().unlock();
                //isReadLock = false;
                rwl.writeLock().lock();
                if (composedPredicate == null) {
                    //write to the "composedPredicate" object
                }
            }
        }finally {
            rwl.writeLock().unlock();
        }
        return composedPredicate.test(taskData);
    }
  1. 当我们只写入数据时,我们真的需要读锁吗?
  2. 上面两个代码有什么区别?
  3. 我们是否应该使用读锁来访问对象(composedPredicate)以进行空检查?

最佳答案

您发布的第一个代码是使用读/写锁的 Java 中双重检查锁定方法的正确实现。

您的第二个没有读锁的实现已损坏。内存模型允许从另一个线程查看内存写入结果的角度对写入进行重新排序。

可能发生的情况是,您可能在读取谓词的线程中使用未完全初始化的谓词实例。

您的代码示例:

我们有线程 A 和 B 都在运行 evaluate,并且composedPredicate 最初为 null

  1. A:看到 composedPredicatenull
  2. A:写锁
  3. A:创建 Predicate 实现的实例
  4. A:在构造函数中初始化此实例
  5. A:将实例分配给共享变量composedPredicate
  6. A:解锁写锁
  1. B:看到 composedPredicatenot null
  2. B:运行 composedPredicate.test(taskData);
  3. 但是,编译器、JVM 或系统的硬件架构重新排序了线程 A 的步骤 4 和 5,并在初始化之前分配了共享字段的 Predicate 实例的地址(这是 Java 允许的)内存模型)
  4. composedPredicate.test(taskData); 使用未完全初始化的实例运行,并且您的代码在生产中出现随机意外错误,从而给您的公司带来巨大损失(这种情况可能会发生..取决于您正在构建的系统)

步骤 4 和 5 的重新排序是否发生取决于许多因素。也许只有在系统负载较重的情况下才会出现这种情况。在您的操作系统、硬件、JVM 版本等上,它可能根本不会发生。(但是在下一个版本的 JVM、您的操作系统上,或者当您将应用程序移动到不同的物理机器时,它可能会突然开始发生)

坏主意。

关于java - 写时读锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48678437/

相关文章:

java - 无法在 Resteasy 和 EJB 3.0 中转换为 javax.ws.rs.core.Application

java - 我如何处理 MongoDB 中的贪婪写锁定?

java - 如何使两个文档根据彼此的输入实时更新?

java - 如何使用ReentrantReadWriteLock等待数据?

java - ReentrantReadWriteLock 上的读锁是否足以并发读取 RandomAccessFile

java - 创建只有 ID 属性的实体表有意义吗?

java - 更新给定父类(super class)的子类

java - 如何从输入中获取 2 个字母(忽略白色字符并且不能使用数组)

java - 在多线程应用程序中,是否应该使用 ReadWriteLock 锁定基本类型的 getter/setter?

Scala 集合循环缓冲区