访问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 启动和停止,状态只是反映了这一点?所以改变状态根本不是当前状态的操作。

解决该设计问题几乎肯定会更容易修复锁定。