线程安全地更新Scala集合

Arg*_*Arg 19 collections concurrency scala thread-safety

我想知道是否有任何'简单'的方法来安全地更新不可变的scala集合.考虑以下代码:

class a {
   private var x = Map[Int,Int]()

   def update(p:(Int,Int)) { x = x + (p) }
}
Run Code Online (Sandbox Code Playgroud)

这段代码不是线程安全的,对吗?我的意思是,如果我们有两个线程调用update方法,并且假设x是包含{1 => 2}的映射,并且线程A调用update((3,4))并且只设法执行x +(p)部分代码.然后重新安排,线程B调用update((13,37))并成功更新变量x.线程A继续并结束.

完成所有这些后,值x将等于包含{1 => 2,3 => 4}的地图,对吗?而不是期望的{1 => 2,3 => 4,13 => 37}.有没有一种简单的方法来解决这个问题?我希望我的要求是不可靠的:)

顺便说一句,我知道有像Akka STM这样的解决方案,但除非必要,否则我宁愿不使用它们.

非常感谢您的回答!

编辑:另外,我更喜欢没有锁定的解决方案.Eeeew :)

Jea*_*let 20

在你的情况下,正如Maurício所写,你的集合已经是线程安全的,因为它是不可变的.唯一的问题是重新分配var,这可能不是原子操作.对于这个特殊问题,最简单的选择是使用好的类java.util.concurrent.atomic,即AtomicReference.

import java.util.concurrent.atomic.AtomicReference

class a {
  private val x = new AtomicReference(Map[Int,Int]())

  def update(p:(Int,Int)) {
    while (true) {
      val oldMap = x.get // get old value
      val newMap = oldMap + p // update
      if (x.compareAndSet(oldMap, newMap))
        return // exit if update was successful, else repeat
    }
  }
}
Run Code Online (Sandbox Code Playgroud)


Mau*_*res 5

集合本身是线程安全的,因为它没有共享可变状态,但您的代码不是,并且没有办法在不锁定的情况下修复此问题,因为您确实具有共享可变状态。最好的选择是锁定方法本身,将其标记为同步。

另一个解决方案是使用可变并发映射,可能是java.util.concurrent.ConcurrentMap