可能出现的线程安全问题
- 数据一致性问题:当多个线程同时修改被观察对象(Subject)的状态时,可能导致观察者(Observer)接收到不一致的数据。例如,在一个简单的计数器应用中,被观察对象的计数器值在多线程环境下可能被错误地修改,导致观察者显示的数值不正确。
- 竞态条件:多个线程可能同时尝试注册或取消注册观察者,这可能导致注册列表出现不一致。比如,线程A在注册观察者时,线程B同时尝试取消同一个观察者的注册,可能导致注册列表处于不一致状态。
- 通知顺序混乱:在多线程环境下,观察者的通知顺序可能变得不可预测。这可能影响依赖于特定通知顺序的业务逻辑。
解决方法及代码示例
- 使用线程安全的数据结构:对于注册和取消注册观察者的操作,可以使用线程安全的集合。例如,
ConcurrentHashMap
来存储观察者。
import java.util.concurrent.ConcurrentHashMap
class Subject {
private val observers = ConcurrentHashMap<Observer, Unit>()
fun registerObserver(observer: Observer) {
observers[observer] = Unit
}
fun unregisterObserver(observer: Observer) {
observers.remove(observer)
}
fun notifyObservers() {
observers.keys.forEach { it.update() }
}
}
interface Observer {
fun update()
}
- 同步块:在关键操作(如修改被观察对象状态和通知观察者)周围使用
synchronized
块。
class Subject {
private val observers = mutableListOf<Observer>()
fun registerObserver(observer: Observer) {
synchronized(this) {
observers.add(observer)
}
}
fun unregisterObserver(observer: Observer) {
synchronized(this) {
observers.remove(observer)
}
}
fun notifyObservers() {
synchronized(this) {
observers.forEach { it.update() }
}
}
}
interface Observer {
fun update()
}
- 使用
Atomic
类型:如果被观察对象的状态是简单数据类型,可以使用Atomic
类型来保证原子性操作。例如,AtomicInteger
。
import java.util.concurrent.atomic.AtomicInteger
class Subject {
private val observers = mutableListOf<Observer>()
private val state = AtomicInteger(0)
fun registerObserver(observer: Observer) {
synchronized(this) {
observers.add(observer)
}
}
fun unregisterObserver(observer: Observer) {
synchronized(this) {
observers.remove(observer)
}
}
fun updateState() {
state.incrementAndGet()
notifyObservers()
}
fun notifyObservers() {
synchronized(this) {
observers.forEach { it.update(state.get()) }
}
}
}
interface Observer {
fun update(state: Int)
}