给定的代码线程是否安全?

时间:2015-07-31 06:53:51

标签: scala

@volatile var aVector = Vector(1, 2, 3)

线程一

aVector +:= getAnInt()

线程二

aVector match {
  case head +: tail =>
    doSomethingWith(head)
    aVector = tail
  case _ =>
}

JVM版本:HotSpot 1.8

Scala版本:2.10.5

2 个答案:

答案 0 :(得分:8)

简答:NO。

+:= 不是原子操作,以及解构+:和赋值。

因此,当出现问题时,您可以有两种情况:

  1. 第一个线程读取Vector,追加一个元素,(此时第二个线程读取Vector,删除元素并重新分配Vector var),第一个线程用附加的Vector重新分配var
  2. 在这种情况下,Vector的第一个元素(由第一个线程附加)将被处理两次。

    1. 第二个线程读取Vector并处理第一个元素(第一个线程开始,读取vector,追加元素),第二个线程使用没有first元素的值重新分配Vector var
    2. 在这种情况下,第一个线程附加的元素将丢失。

      如何使这段代码线程安全:

      1. 您可以使用java的并发队列(可能是最好和最简单的方法)
      2. 如果你想使用scala-way,你应该考虑使用actor作为生产者 - 消费者
      3. 您可以设计自己的同步解决方案
      4. UPD

        关于@volatile的一些说明以及对不可变集合的操作。 Scala注释@volatile基本上是java的volatile关键字。在这种情况下,它会使var aVector原子分配并在其他线程中立即可见,但它不会使(读取 - 更新 - 分配操作)序列成为原子或同步。

        此scala代码:

        @volatile var aVector = Vector(1, 2, 3)
        aVector +:= 1
        

        编译到这个java:

        public final class _$$anon$1 {
          private volatile Vector<Object> aVector = package$.MODULE$.Vector().apply(Predef$.MODULE$.wrapIntArray(new int[] { 1, 2, 3 }));
        
          private Vector<Object> aVector() {
            return (Vector<Object>)this.aVector;
          }
        
          private void aVector_$eq(final Vector<Object> x$1) {
            this.aVector = x$1;
          }
        
          {
            this.aVector_$eq(this.aVector().$plus$colon(BoxesRunTime.boxToInteger(1), Vector$.MODULE$.canBuildFrom()));
          }
        }
        

        如您所见,Vector通过非原子序列的函数调用进行读取,更新和分配。第二个线程可以在中间更新它。

答案 1 :(得分:0)

下面的代码显示它在行动中不是线程安全的,但我仍然对易失性注释感到困惑。

object TryThreadSafe {
  def main(args: Array[String]) {
    @volatile var aVector = Vector(1, 2, 3)
    val thread = new Thread(new Runnable {
      override def run(): Unit = {
        aVector :+= getAnInt()
      }
    })
    thread.start()
    while (aVector.nonEmpty) {
      aVector match {
        case head +: tail =>
          doSomethingWith(head)
          aVector = tail
        case _ =>
      }
    }
    thread.join()
    while (aVector.nonEmpty) {
      aVector match {
        case head +: tail =>
          doSomethingWith(head)
          aVector = tail
        case _ =>
      }
    }
    //print 1 2 3 1 2 3 4
  }

  def getAnInt() = {
    Thread.sleep(10000)
    4
  }

  def doSomethingWith(head: Int) = {
    println(head)
  }
}