写作

时间:2018-02-08 05:47:02

标签: 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);
    }

如果我们在上面的代码中没有使用Read Locks,会发生什么? 喜欢:

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. 我们是否应该使用Read锁来访问对象(composedPredicate)以进行空检查?

2 个答案:

答案 0 :(得分:1)

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

没有读锁定的第二个实现被破坏了。内存模型允许写入从另一个线程的角度重新排序,看到写入内存的结果。

可能发生的是你可能在正在读取它的线程中使用了一个未完全初始化的Predicate实例。

代码示例:

我们的线程A和B都在运行evaluate,而compositionPredicate最初都是null

  1. 答:看到composedPredicatenull
  2. A:write-locks
  3. 答:创建Predicate
  4. 的实施实例
  5. 答:在构造函数
  6. 中初始化此实例
  7. 答:将实例分配给共享变量composedPredicate
  8. A:解锁写锁定
    1. B:看到composedPredicate null
    2. B:运行composedPredicate.test(taskData);
    3. 但是,系统的编译器,JVM或硬件体系结构重新排序了线程A的第4步和第5步,并在初始化之前为共享字段的Predicate实例分配了地址(这是Java允许的)记忆模型)
    4. composedPredicate.test(taskData);使用未完全初始化的实例运行,并且您的代码在生产中出现随机意外错误,从而导致贵公司遭受重大损失(可能会发生这种情况......取决于您正在构建的系统)
    5. 步骤4和5的重新排序是否取决于许多因素。也许它只能在繁重的系统负载下运行。在您的操作系统,硬件,JVM版本等上可能根本不会发生这种情况(但是在下一版本的JVM,您的操作系统或将应用程序移动到其他物理机器时,它可能会突然开始发生)< / p>

      糟糕的主意。

答案 1 :(得分:-1)

此代码类似于旧的&#39; Singleton模式&#39;使用同步块。 E.g。

class Singleton
{
    Singleton s;

    public static Singleton instance()
    {
        if(s == null)
        {
             synchronized(Singleton.class)
             {
                  if(s == null)
                      s = new Singleton();
             }
         }
         return s;
    }
}

注意双重&#39;空检查&#39;只有第二个同步的地方。进行第一次“无效检查”的原因是&#39;是为了防止在调用instance()方法时阻塞线程(因为当不为null时,它可以在没有同步的情况下继续)。

您的第一个代码也在做同样的事情。首先,它检查是否有分配给composedPredicate的内容。如果不是这样的话,那么它只会获取一个writLock(它阻止所有其他Thread选择readLocks,它只会阻塞writeLocks)。

与单身模式&#39;的主要区别。而你的代码是你的价值可以重新确定。只有确保在修改过程中没有人读取值时,才能安全地进行此操作。通过删除readLock,您基本上可以创建一个线程在访问composedPredicate时可能获得未定义结果(如果不是崩溃)而另一个线程正在修改该相同字段的可能性。

所以回答你的问题: 1.你不需要一个readLock来写,只需要一个writeLock(它会阻止所有其他试图锁定的线程)。但在这种设计模式中,你不能把它排除在外。 2.&amp; 3.见上面的解释。

希望这足以掌握这种模式。