仅当在 Scala 中使用不相关的对象时,块才会同步

Block is synchronised only when using unrelated object in Scala

我有一个函数 getUniqueId 试图在每次调用时 return 一个唯一的 ID。

private var uidCount = 0L

def getUniqueId() = uidCount.synchronized {
  uidCount = uidCount + 1
  uidCount
}

它的定义在 Long 变量 uidCount 上同步。

我们正在使用另一个函数调用此函数 startThread

def startThread() = {
  val t = new Thread {
    override def run() = {
      val uids = for (i <- 0 until 10) yield getUniqueId()
      println(uids)
    }
  }
  t.start
}

因此,对于每个函数调用,理想情况下我们应该在控制台上看到 10 个唯一 ID。

我们使用多线程调用 ID,如下所示:
startThread(); startThread(); startThread()

但是我得到了带有重复 ID 的输出: 输出:

Vector(7, 11, 13, 16, 22, 24, 25, 26, 27, 28)
Vector(2, 6, 8, 9, 12, 14, 17, 19, 21, 23)
Vector(1, 3, 4, 5, 8, 10, 12, 15, 18, 20)

这里我们可以看到8和12是重复的

类似地,如果我在 Class AnyRef 的随机对象上同步 getUniqueId()
即我将定义更改为:

private var uidCount = 0L
private var x = new AnyRef
def getUniqueId() = x.synchronized {
  uidCount = uidCount + 1
  uidCount
}

然后突然之间 getUniqueId 是原子的。输出总是类似于:

Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Vector(11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
Vector(21, 22, 23, 24, 25, 26, 27, 28, 29, 30)

即没有重复。

有人可以解释为什么我不能使用 uidCount 使 getUniqueId 原子化吗?

uidCount是一个随时都在变化的原始变量

每次调用 getUniqueId 时,都会围绕原始值创建一个新的包装器。在每次进入方法体时都重新创建的新对象上同步没有任何意义:与该对象关联的内在锁将始终恰好获取一次,然后立即释放,包装对象将被丢弃.