访问synchronized 属性后,两个线程能否同时访问同一个方法?
Can two threads access same method at once after access of synchronized property?
我对我的代码中的某些情况有点困惑。
private var state by synchronized<PlayerState>(Stopped())
val asStarted get() = state as? Started
val asStopped get() = state as? Stopped
inner class Stopped : PlayerState {
fun start(bpm: BPM = BPM(), volume: Int? = null) {
state = Started(bpm, volume)
}
}
假设有两个线程正在尝试执行 player.asStopped?.start()
,我这里有问题吗?我需要输入这样的内容吗?
inner class Stopped : PlayerState {
@Synchronized
fun start(bpm: BPM = BPM(), volume: Int? = null) {
if(state is Started) return
state = Started(bpm, volume)
}
}
感谢帮助。
你的担心是对的。
您的代码的第一个版本确实存在竞争条件,因为两个线程都可以创建 Started
个对象,然后设置它们。一次只能有一个线程执行 setter,但第二个可以在第一个线程离开后立即进入。
这在这里可能不是问题。 (synchronized
委托仍然会防止访问 state
时出现任何低级问题,例如看到部分构造的值。似乎没有其他状态需要与您的 state
属性,所以不会进入不一致的状态。)‖ 但是能够启动两次未必是好事!
我认为您的代码的第二个版本没有任何线程问题,如所写。 (如果它没有 @Synchronized
,那么它确实会有 time-of-check-to-time-of-use 竞争条件。)
然而,它有点令人困惑,因为它使用了两个独立的锁定机制(@Synchronized
方法锁定在 Stopped
对象上,而 by synchronized
state 有自己的锁),即使在这个非常简单的例子中,也很难理解这些锁可能交互的方式。
我还怀疑它可能会导致周围代码中出现竞争条件。一旦启动,您的播放器可以再次停止吗?如果是这样,stop()
方法将锁定什么——如果它是 Started
对象,那么这是另一个锁定,我可以在那里看到一些竞争条件。
所以我会强烈考虑重构它以仅使用一个锁。
我不知道您的代码正在使用 synchronized
委托。如果它具有某种测试和设置、比较和交换或类似功能,那么您可以使用它来完成您需要的操作。否则,恐怕最好放弃委托并自行锁定。
这确实提出了一个关于设计的更广泛的问题,不过:为什么你的 Stopped
class 有一个 start()
方法??肯定是 player 启动和停止,状态只是反映了这一点?所以改变状态根本不是对当前状态的操作。
解决该设计问题几乎肯定会更容易修复锁定。
我对我的代码中的某些情况有点困惑。
private var state by synchronized<PlayerState>(Stopped())
val asStarted get() = state as? Started
val asStopped get() = state as? Stopped
inner class Stopped : PlayerState {
fun start(bpm: BPM = BPM(), volume: Int? = null) {
state = Started(bpm, volume)
}
}
假设有两个线程正在尝试执行 player.asStopped?.start()
,我这里有问题吗?我需要输入这样的内容吗?
inner class Stopped : PlayerState {
@Synchronized
fun start(bpm: BPM = BPM(), volume: Int? = null) {
if(state is Started) return
state = Started(bpm, volume)
}
}
感谢帮助。
你的担心是对的。
您的代码的第一个版本确实存在竞争条件,因为两个线程都可以创建 Started
个对象,然后设置它们。一次只能有一个线程执行 setter,但第二个可以在第一个线程离开后立即进入。
这在这里可能不是问题。 (synchronized
委托仍然会防止访问 state
时出现任何低级问题,例如看到部分构造的值。似乎没有其他状态需要与您的 state
属性,所以不会进入不一致的状态。)‖ 但是能够启动两次未必是好事!
我认为您的代码的第二个版本没有任何线程问题,如所写。 (如果它没有 @Synchronized
,那么它确实会有 time-of-check-to-time-of-use 竞争条件。)
然而,它有点令人困惑,因为它使用了两个独立的锁定机制(@Synchronized
方法锁定在 Stopped
对象上,而 by synchronized
state 有自己的锁),即使在这个非常简单的例子中,也很难理解这些锁可能交互的方式。
我还怀疑它可能会导致周围代码中出现竞争条件。一旦启动,您的播放器可以再次停止吗?如果是这样,stop()
方法将锁定什么——如果它是 Started
对象,那么这是另一个锁定,我可以在那里看到一些竞争条件。
所以我会强烈考虑重构它以仅使用一个锁。
我不知道您的代码正在使用 synchronized
委托。如果它具有某种测试和设置、比较和交换或类似功能,那么您可以使用它来完成您需要的操作。否则,恐怕最好放弃委托并自行锁定。
这确实提出了一个关于设计的更广泛的问题,不过:为什么你的 Stopped
class 有一个 start()
方法??肯定是 player 启动和停止,状态只是反映了这一点?所以改变状态根本不是对当前状态的操作。
解决该设计问题几乎肯定会更容易修复锁定。