可变原子引用是个坏主意吗?
Are Mutable Atomic References a Bad Idea?
我有一个数据结构,我偶尔希望修改,偶尔希望完全替换。目前,我将它存储在 AtomicReference 中,并在需要修改它而不是替换它时使用同步块(在 AtomicReference 本身上同步,而不是它的存储值)。
所以像这样:
public void foo(AtomicReference reference){
synchronized(reference){
reference.get()
.performSomeModification();
}
}
请注意,修改调用是包装值的成员,而不是原子引用,并且不能保证其自身具有任何线程安全性。
这里安全吗? Findbugs(一种免费软件代码审查工具)已经 this 对此进行了说明,所以现在我担心引擎盖下发生了一些事情,它可能会过早地释放锁或其他东西。我还看到了引用 AtomicReference 的文档专门用于不可变的东西。
这里安全吗?如果不是,我可以创建自己的参考存储 class,我会更确定其行为,但我不想仓促下结论。
来自链接文档:
For example, synchronizing on an AtomicBoolean
will not prevent other threads from modifying the AtomicBoolean
.
它不能阻止其他线程修改AtomicBoolean
,因为它不能强制其他线程在AtomicBoolean
上同步。
如果我没有正确理解你的问题,你的意图是同步对 performSomeModification()
的调用。您编写的代码将实现这一点,当且仅当 对 performSomeModification()
的每次调用都在同一对象上同步。正如文档中的示例一样,基本问题是该要求的可执行性。您不能强制其他呼叫者在 AtomicReference
上同步。您或您之后的其他开发人员可以轻松调用 performSomeModification()
而无需外部同步。
你应该让 API 的错误使用变得困难。由于 AtomicReference
是通用类型 (AtomicReference<V>
),您可以通过多种方式强制执行同步,具体取决于 V
是什么:
- 如果
V
是一个接口,您可以轻松地将实例包装在同步包装器中。
- 如果
V
是一个您可以修改的 class,您可以同步 performSomeModification()
,或者创建一个子 class 在其中进行同步。 (可能是工厂方法生成的匿名子class。)
- 如果
V
是一个你不能修改的class,它可能很难换行。在这种情况下,您可以将 AtomicReference
封装在您控制的 class 中,并让 class 执行所需的同步。
Are Mutable Atomic References a Bad Idea?
绝对不是! AtomicReference
旨在提供底层引用的线程安全、原子更新。事实上,Javadoc description of AtomicReference 是:
An object reference that may be updated atomically.
所以它们绝对是为变异而设计的!
Is this safe?
这取决于您所说的 "safe" 是什么意思,以及您的其余代码在做什么。孤立的代码片段本身并没有什么不安全的地方。在 AtomicReference 上同步是完全有效的,尽管可能有点不寻常。作为不熟悉此代码的开发人员,我会看到 reference
上的同步并假设这意味着底层对象可能随时被替换,并且您希望确保您的代码始终在 "newest"参考。
同步的标准最佳实践适用,违反它们可能会导致不安全行为。例如,由于您说 performSomeModification()
不是线程安全的,如果您在其他地方访问基础对象而不在 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();
}
If 也可以是 "unsafe" 如果您的应用程序要求在任何时候只对底层对象的一个实例进行操作,并且您在 .set() 时没有同步引用:
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 上进行同步,那么这样做是非常安全的。但我强烈建议添加一些代码注释,说明您这样做的原因。
我有一个数据结构,我偶尔希望修改,偶尔希望完全替换。目前,我将它存储在 AtomicReference 中,并在需要修改它而不是替换它时使用同步块(在 AtomicReference 本身上同步,而不是它的存储值)。
所以像这样:
public void foo(AtomicReference reference){
synchronized(reference){
reference.get()
.performSomeModification();
}
}
请注意,修改调用是包装值的成员,而不是原子引用,并且不能保证其自身具有任何线程安全性。
这里安全吗? Findbugs(一种免费软件代码审查工具)已经 this 对此进行了说明,所以现在我担心引擎盖下发生了一些事情,它可能会过早地释放锁或其他东西。我还看到了引用 AtomicReference 的文档专门用于不可变的东西。
这里安全吗?如果不是,我可以创建自己的参考存储 class,我会更确定其行为,但我不想仓促下结论。
来自链接文档:
For example, synchronizing on an
AtomicBoolean
will not prevent other threads from modifying theAtomicBoolean
.
它不能阻止其他线程修改AtomicBoolean
,因为它不能强制其他线程在AtomicBoolean
上同步。
如果我没有正确理解你的问题,你的意图是同步对 performSomeModification()
的调用。您编写的代码将实现这一点,当且仅当 对 performSomeModification()
的每次调用都在同一对象上同步。正如文档中的示例一样,基本问题是该要求的可执行性。您不能强制其他呼叫者在 AtomicReference
上同步。您或您之后的其他开发人员可以轻松调用 performSomeModification()
而无需外部同步。
你应该让 API 的错误使用变得困难。由于 AtomicReference
是通用类型 (AtomicReference<V>
),您可以通过多种方式强制执行同步,具体取决于 V
是什么:
- 如果
V
是一个接口,您可以轻松地将实例包装在同步包装器中。 - 如果
V
是一个您可以修改的 class,您可以同步performSomeModification()
,或者创建一个子 class 在其中进行同步。 (可能是工厂方法生成的匿名子class。) - 如果
V
是一个你不能修改的class,它可能很难换行。在这种情况下,您可以将AtomicReference
封装在您控制的 class 中,并让 class 执行所需的同步。
Are Mutable Atomic References a Bad Idea?
绝对不是! AtomicReference
旨在提供底层引用的线程安全、原子更新。事实上,Javadoc description of AtomicReference 是:
An object reference that may be updated atomically.
所以它们绝对是为变异而设计的!
Is this safe?
这取决于您所说的 "safe" 是什么意思,以及您的其余代码在做什么。孤立的代码片段本身并没有什么不安全的地方。在 AtomicReference 上同步是完全有效的,尽管可能有点不寻常。作为不熟悉此代码的开发人员,我会看到 reference
上的同步并假设这意味着底层对象可能随时被替换,并且您希望确保您的代码始终在 "newest"参考。
同步的标准最佳实践适用,违反它们可能会导致不安全行为。例如,由于您说 performSomeModification()
不是线程安全的,如果您在其他地方访问基础对象而不在 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();
}
If 也可以是 "unsafe" 如果您的应用程序要求在任何时候只对底层对象的一个实例进行操作,并且您在 .set() 时没有同步引用:
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 上进行同步,那么这样做是非常安全的。但我强烈建议添加一些代码注释,说明您这样做的原因。