我有一堂课,在很大程度上简化了相关部分,看起来像这样(虚拟名称):
@RequiredArgsConstructor
public class SomeClass {
private final SomeProvider someProvider;
private SomeDataStore store = null;
private SomeDataStore getStoreAttributes() {
if (store == null) {
store = new SomeDataStore(someProvider, <other params>)
}
return store;
}
}
一个队友评论说store == null
的检查不是线程安全的,因为store
的值可能在线程之间改变。但是,我们不确定这是否重要,因为没有任何事情将store
设置为null,并且我没有看到多个线程尝试将相同的store
设置为new SomeDataStore( ...),因为数据存储区对代码而言是只读的。
我的线程安全性是否缺少任何问题?
谢谢!
答案 0 :(得分:2)
我的线程安全性是否缺少任何问题?
是的。至少这些:
如果您有两个或多个线程共享的SomeClass
实例,则这些线程中至少有一个曾经将store
成员设置为非null
值,并且任何其他线程在该实例上调用getStoreAttributes()
,那么您将发生数据竞争。在这种情况下,程序的行为是不确定的。
在诸如此类的数据竞赛中可以观察到的行为中
store
store
的更新即使您可以假设对store
的读取和写入都是原子的,因此没有数据争用,您 still 仍然会遇到两个不同的线程可以两者都观察到store
为null
并同时输入if
块。结果,他们每个人都会实例化一个新的SomeDataStore
,该方法将返回该新fieldset
,从而破坏了明显的目的。
答案 1 :(得分:1)
在给定的示例中,如果有多个线程在调用方法“ getStoreAttributes()”,则“ store”实例变量可以初始化两次。 如果您认为多次初始化存储变量是可以的,那么就可以了,无需添加任何线程安全性。 如果您不想添加诸如同步块之类的线程安全功能,请至少使“存储”变量易失,这将确保线程将从内存中读取其最新状态。
但是从“存储”这个名字来看,它似乎是一些资源,并且无缘无故地多次初始化似乎并不正确。您可以通过增加线程安全性来节省它。 如果一个线程正在初始化资源,其他线程将等待并使用初始化的资源。 您可以实施双重锁定以确保相同。
答案 2 :(得分:0)
简短的答案是,您应该努力以使其线程安全的方式编写代码。现在,答案很长:据您所知,由于这种不安全的操作,您的代码目前没有问题。假设您是一个神童,并且已对当前情况进行了正确的评估,则通过将其纳入代码库,您已经添加了某种形式的债务,某些代码可能会在将来容易证明有问题,并且可能会导致查找困难错误。很难说,但是我看到您正在尝试创建一个缓存的字段,有一些方法可以使此线程安全。