我有一个类,它有另一个类的内部实例。
@Singleton
class ClassA {
private var classBInstance: ClassB
init{
classBInstance = ClassB()
}
fun performOperation(): Result {
return classBInsatnce.doSomething()
// doSomething() takes about 4 seconds
}
fun updateClassB() {
classBInstance = ClassB()
}
}
ClassA 是一个单例,由多个线程使用,并在其上调用performOperation()。 现在,如果消费者调用performOperation,从而在内部调用classBInstance.doSomething(),并且在处理过程中(持续4秒),如果其他人调用updateClassB(),则变量classBInstance现在持有一个新实例,如果B 类。那么前面的消费者会发生什么,他的操作 (doSomething() 仍在 ClassB 的前一个实例中进行。
我问,因为我的想法是,我想确保当 classBInstance 持有的实例更新为新实例时,performOperation() 处理的任何现有调用都是使用 ClassB 的旧实例完成的,并且仅在 ClassB 新实例之后完成被设置到 classBInstance 变量中,那么只有新的实例 classB 用于对 PerformOperation() 的任何进一步调用
有人可以帮忙澄清一下吗? 谢谢
我尝试阅读有关类实例及其操作方式的信息,但对这种边缘情况感到困惑
在大多数情况下,Java 内存模型 应该适用于此。
首先,我假设您的单例是以线程安全的方式创建的(例如,它是一个
val
)。
我发现您的代码存在两个问题:
ClassB
的“不安全出版”。当您执行 classBInstance = ClassB()
时,可能会发生指令重新排序并且分配发生在 ClassB
完全初始化之前。classBInstance
,由于 CPU 缓存的原因,它可能对其他线程不可见。您需要确保 classBInstance
已更新。您可以通过用
@Volatile标记
classBInstance
来解决这两个问题。这确保了可见性,并在访问之间建立了发生之前的关系。当一个线程设置 classBInstance
时,此操作将在进一步读取变量之前发生,并且 JVM 将确保后续读取(由其他线程)读取更新的值。