java - 访问同步属性后,两个线程可以同时访问同一个方法吗?
问题描述
我对代码中的某些情况有点困惑。
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
状态有自己的锁定),并且很难遵循这些锁定可能交互的方式,即使在这个非常简单的示例中也是如此。
我还怀疑它可能导致周围代码中的竞争条件。一旦开始,您的播放器可以再次停止吗?如果是这样,stop()
方法将锁定什么 - 如果是Started
对象,那么那是另一个锁定,我可以在那里看到一些竞争条件。
因此,我强烈建议将其重构为仅使用一个锁。
我不知道synchronized
您的代码正在使用的委托。如果它具有某种测试和设置、比较和交换或类似功能,那么您可以使用它来做您需要的事情。否则,恐怕最好放弃委托并自己进行锁定。
不过,这确实引发了一个关于设计的更广泛的问题:为什么你的Stopped
类有一个start()
方法? 当然是玩家开始和停止,而状态只是反映吗?所以改变状态根本不是对当前状态的操作。
解决该设计问题几乎肯定会使修复锁定变得更容易。
推荐阅读
- python - Google Colaboratory NotFoundError: /usr/local/lib/python3.7/dist-packages/tensorflow_text/python/metrics/_text_similarity_metric_ops.so
- java - 我们如何让两个线程(可以访问相同的共享实例 obj)执行不同的逻辑?
- javascript - 三.JS TorusGeometry with MeshStandardMaterial 失去质量
- sql - 带有学生姓名的每门课程的最高分
- node.js - MERN : 页面刷新后 Cookie 删除
- python - 有没有办法跳过空的 Google 工作表值并继续关注下一行?包括部分工作脚本
- java - CachingModelPropertiesProvider 使 Spring Boot 启动延迟会产生大量警告
- sql - 如何在 oracle 中操作 SQL ROWNUM?
- python - 如何使用双击事件(例如添加标记、取消先前的操作)与 matplotlib 图交互?
- javascript - Node js和mongoDB从回调函数返回对象