3

我有一个数据结构,我偶尔希望修改,偶尔希望彻底替换。目前,我将它存储在 AtomicReference 中,并在我需要修改它而不是替换它时使用同步块(在 AtomicReference 本身上同步,而不是它的存储值)。

所以像:

public void foo(AtomicReference reference){
    synchronized(reference){
        reference.get()
            .performSomeModification();
    }
}

请注意,修改调用是包装值的成员,而不是原子引用,并且不能保证其自身具有任何线程安全性。

这安全吗?Findbugs(一个免费的代码审查工具)有这个说法,所以现在我担心引擎盖下发生了一些事情,它可能会过早地释放锁或其他东西。我还看到将 AtomicReference 引用为专门用于不可变事物的文档。

这安全吗?如果不是,我可以创建我自己的引用存储类,我会更确定它的行为,但我不想草率下结论。

4

2 回答 2

4

从链接的文档中:

例如,在 an 上同步AtomicBoolean不会阻止其他线程修改AtomicBoolean.

它不能阻止其他线程修改 ,AtomicBoolean因为它不能强制其他线程在AtomicBoolean.

如果我正确理解您的问题,您的意图是将调用同步到performSomeModification(). 当且仅当每次调用performSomeModification()都在同一个对象上同步时,您编写的代码才能实现这一点。正如文档中的示例一样,基本问题是该要求的可执行性。您不能强制其他调用者在AtomicReference. 您或其他跟随您的开发人员可以轻松调用performSomeModification()而无需外部同步。

您应该很难错误地使用您的 API。由于AtomicReference是泛型类型 ( AtomicReference<V>),您可以通过多种方式强制同步,具体取决于V

  • 如果V是一个接口,您可以轻松地将实例包装在同步包装器中。
  • 如果V是您可以修改的类,您可以 synchronize performSomeModification(),或创建一个在其中同步它的子类。(可能是工厂方法产生的匿名子类。)
  • 如果V是您无法修改的类,则可能难以包装。在这种情况下,您可以将 封装AtomicReference在您控制的类中,并让该类执行所需的同步。
于 2016-09-09T00:30:17.683 回答
1

可变原子引用是个坏主意吗?

当然不!AtomicReference旨在提供底层引用的线程安全、原子更新。实际上,AtomicReference 的 Javadoc 描述是:

可以自动更新的对象引用。

所以它们绝对是为了变异而设计的!

这安全吗?

这取决于您所说的“安全”是什么意思,以及您的其余代码在做什么。孤立的代码片段本身并没有什么不安全的地方。在 AtomicReference 上同步是完全有效的,尽管可能有点不寻常。作为不熟悉此代码的开发人员,我会看到同步reference并假设这意味着可能随时替换底层对象,并且您希望确保您的代码始终在“最新”引用上运行。

适用于同步的标准最佳实践,违反它们可能会导致不安全的行为。例如,由于您说performSomeModification()不是线程安全的,因此如果您在其他地方访问底层对象而不同步 on ,那reference是不安全的。

public void bar(AtomicReference reference) {
    // no synchronization: performSomeModification could be called on the object 
    // at the same time another thread is executing foo()
    reference.get().performSomeModification();

}

如果您的应用程序要求在任何时候只对底层对象的一个​​实例进行操作,并且您在 .set() 时没有在引用上同步它,那么 if 也可能是“不安全的”:

public void makeNewFoo(AtomicReference reference) {
    // no synchronication on "reference", so it may be updated by another thread 
    // while foo() is executing performSomeModification() on the "old" reference
    SomeObject foo = new SomeObject();
    reference.set(foo);
}

如果您需要在 AtomicReference 上进行同步,那么这样做是非常安全的。但我强烈建议您添加一些代码注释来说明您这样做的原因。

于 2016-09-09T00:49:02.137 回答