@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
答案 0 :(得分:8)
简答:NO。
+:=
不是原子操作,以及解构+:
和赋值。
因此,当出现问题时,您可以有两种情况:
var
),第一个线程用附加的Vector重新分配var
。 在这种情况下,Vector的第一个元素(由第一个线程附加)将被处理两次。
var
。 在这种情况下,第一个线程附加的元素将丢失。
如何使这段代码线程安全:
关于@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)
}
}