在Scala对象中,一个不可变的val是否线程安全?

6
object Users {
  val userCountByAgeMap = readFromFile(); // read from file returns immutable map
}

在上面的Scala示例中,用户将是单例对象,并且userCountByAgeMap将被懒加载初始化。
那么这个初始化是原子的吗?即只有一个线程可以初始化它。假设userCountByAgeMap由Thread A初始化,是否会对Thread B可见?
如果初始化不是原子的/内存可见性不能保证,将userCountByAgeMap变量设置为lazy val是否能解决问题?
2个回答

4

在Scala中,对象是在静态块中初始化的,因此JVM可以保证线程安全(Java静态初始化器是线程安全的)。您可以使用JAD反编译器来分析字节码。以下是代码:

object Users {
  val userCountByAgeMap = Map.empty[String, Int]
}

解密Users$.class文件:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   Users.scala

import scala.Predef$;
import scala.collection.immutable.Map;
import scala.collection.immutable.Map$;

public final class Users$
{

    public Map userCountByAgeMap()
    {
        return userCountByAgeMap;
    }

    private Users$()
    {
        userCountByAgeMap = Predef$.MODULE$.Map().empty();
    }

    public static final Users$ MODULE$ = this;
    private final Map userCountByAgeMap;

    static 
    {
        new Users$();
    }
}

由于您正在使用自动提供线程安全的不可变 Map,因此可以从不同的线程访问该字段,这是安全的。


2
是的,在Scala中,object中的val是线程安全的,您不需要将其更改为lazy val来实现thread safe。正如@Artavazd Balayan所说,Scalaobject等同于Java的单例对象。因此它等同于:
class Users {
    public static Users users;
    static {
      users = new Users();
    }
    val userCountByAgeMap = readFromFile(); // read from file returns immutable map
}

我们知道,当类(Users)被加载时,Java的静态块static block将被初始化,因此它是线程安全的。
需要指出的是,lazy val的线程安全性是为了解决字段是懒加载的问题,并且只有在调用时才初始化。但是,在通过Double checking locking进行初始化时,它仍然保持多个线程之间的线程安全性。

http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接