繁体   English   中英

这是双重检查锁定吗?

[英]Is this broken double checked locking?

Checkstyle将此代码报告为“双重检查锁定习语已损坏”,但我认为我的代码实际上并未受到双重检查锁定问题的影响。

如果不存在具有该id的行,则该代码应该在数据库中创建一行。 它在多线程环境中运行,我想避免主键存在的SQL异常。

伪代码:

private void createRow(int id) {
  Row row = dao().fetch(id);
  if (row == null) {
     synchronized (TestClass.class) {
        row = dao().fetch(id);
        if (row == null) {
           dao().create(id);
        }
     }
  }
}

我可以同意它看起来像双重检查锁定,但我没有使用静态变量,fetch()和create()中的代码可能太复杂,无法内联并使其无序。

我错了还是格式? :)

我认为在这种情况下,checkstyle是正确的。 在您提供的代码中,考虑如果两个线程在synchronized块的条目处都有row == null会发生什么。 线程A将进入块,并插入新行。 然后在线程A退出块后,线程B将进入块(因为它不知道刚刚发生了什么),并尝试再次插入相同的新行。

我看到你刚刚更改了代码,并在那里添加了一个非常重要的缺失行。 代码中,您可能能够避免这种情况,因为两个线程不会依赖于对共享(静态)变量的更改。 但是,如果您的DBMS支持诸如INSERT OR UPDATE类的语句,您可能会更好。

将此功能委派给DBMS的另一个好理由是,您需要部署多个应用程序服务器。 由于synchronized块不能跨机器工作,因此无论如何都必须在其他情况下执行其他操作。

假设您想要读取最里面的行:

row = dao().create(id);

假设dao().fetch与create方法正确互斥,这不是经典的双重检查锁定问题。

编辑 :(代码已更新)

双重检查锁的经典问题是在初始化发生之前分配一个值,其中两个线程正在访问相同的值。

假设DAO正确同步并且不会返回部分初始化的值,这不会受到双重检查锁定习语的缺陷的影响。

如果你想写这样的代码,请考虑:

  • 从Java 1.4开始,同步方法变得相当便宜。 它不是免费的,但运行时确实没有那么多,以至于值得冒着数据损坏的风险。

  • 从Java 1.5开始,您就拥有了允许您以原子方式读取和设置字段的Atomic *类。 不幸的是,它们无法解决您的问题。 为什么他们没有添加AtomicCachedReference或其他东西(当调用get()并且当前值== null时会调用overridable方法)超出我的范围。

  • 试试ehcache 它允许您设置缓存(即,如果某个键包含在地图中,则允许您调用代码的对象)。 这通常是你想要的,缓存真正解决你的问题(以及你不知道甚至存在的所有其他问题)。

正如其他人所指出的那样,这段代码将按照您的意图行事,但仅限于一系列严格的非显而易见的假设:

  1. Java代码是非集群的(参见@Greg H的答案)
  2. 在同步块之前的第一行中检查“行”引用是否为空。

双重检查锁定习惯用法被破坏的原因(根据实践中Java并发部分16.2.4)是运行此方法的线程可能在进入“行”之前看到非空但未正确初始化的对“行”的引用。 synchronized块(除非“dao”提供正确的同步)。 如果您的方法使用“row”执行任何操作,而不是检查它是否为null,则它将被破坏。 就目前而言,它可能还可以,但非常脆弱 - 如果我认为甚至有一些其他开发人员稍后可能会在不了解DCL的微妙之处的情况下修改方法,我个人也不会轻易提交此代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM