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

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);
    }



仅写数据时,我们真的需要读锁吗?
上面两个代码有什么区别?
我们是否应该甚至使用读取锁来访问对象(composedPredicate)进行null检查?

最佳答案

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

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

可能发生的事情是,您可能正在正在读取它的线程中使用未完全初始化的Predicate实例。

您的代码示例:

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


答:看到composedPredicatenull
答:写锁
答:创建Predicate实现的实例
答:在构造函数中初始化此实例
答:将实例分配给共享变量composedPredicate
答:解锁写锁





B:看到composedPredicate不是null
B:运行composedPredicate.test(taskData);
但是,系统的编译器,JVM或硬件体系结构对线程A的步骤4和5进行了重新排序,并在初始化共享字段之前为其分配了谓词实例的地址(Java内存模型允许这样做)
composedPredicate.test(taskData);使用未完全初始化的实例运行,并且您的代码在生产中具有随机的意外错误,从而给公司造成巨大损失(可能发生的情况..取决于您所构建的系统)


步骤4和5的重新排序是否发生取决于许多因素。也许只有在系统负载很重的情况下才可以。它可能根本不会在您的OS,硬件,JVM版本等上发生。(但是,在下一版本的JVM,您的OS上,或者当您将应用程序移动到另一台物理计算机上时,它可能突然开始发生)

馊主意。

08-05 13:13