多线程下Singleton类ClassA替换ClassB实例的执行逻辑疑问
我有一个内部持有另一个类实例的单例类ClassA,代码如下:
@Singleton class ClassA { private var classBInstance: ClassB init{ classBInstance = ClassB() } fun performOperation(): Result { return classBInstance.doSomething() // doSomething() takes about 4 seconds } fun updateClassB() { classBInstance = ClassB() } }
ClassA是单例类,被多个线程调用其performOperation()方法。当某个线程调用performOperation()后,内部执行classBInstance.doSomething()(该方法耗时约4秒),此时若另一线程调用updateClassB()将classBInstance替换为新的ClassB实例,我有以下两个疑问:
- 正在执行的
doSomething()是否会继续用旧的ClassB实例完成全部操作,后续请求才使用新实例? - 替换
classBInstance变量后,旧ClassB实例的doSomething()是否会出现问题?
我的期望是:更新classBInstance为新实例时,正在处理的请求仍使用旧实例,只有新请求才使用新实例,特此请教。
问题解答
1. 正在执行的doSomething()会持续使用旧实例
当线程调用performOperation()时,会先读取classBInstance的当前引用(旧实例),之后调用该实例的doSomething()方法。哪怕后续有线程通过updateClassB()替换了classBInstance的引用,已经拿到旧实例引用的线程会继续用这个旧实例完成整个doSomething()的执行流程,只有新发起的performOperation()调用才会读取新的classBInstance引用并使用新实例。
2. 旧实例的doSomething()不会出现异常问题
只要doSomething()方法本身是线程安全的(比如没有未同步的共享可变状态),旧实例的方法执行不会因为classBInstance的引用被替换而受影响。classBInstance变量只是存储了实例的引用,替换引用并不会销毁旧实例——旧实例会一直存在直到没有任何线程持有它的引用(之后会被垃圾回收器回收),在此之前它的方法执行完全不受影响。
注意事项
当前代码中的classBInstance变量没有做线程同步处理,在多线程环境下可能存在可见性问题:比如某个线程更新了classBInstance后,其他线程可能无法立即看到新的引用,导致继续使用旧实例。如果需要确保新实例能被所有线程及时感知,建议给classBInstance添加@Volatile注解(Kotlin),或者通过同步代码块来保证读写操作的可见性与原子性。
内容的提问来源于stack exchange,提问作者Sunny

