Java 静态块线程安全



我正在研究Java 11并发模型,我看到可以通过LazyHolder模式获得真正的单例。特别是,该页面说:

由于 JLS 保证类初始化阶段为 顺序,即非并发,没有进一步的同步 必填。。。

好的,所以我了解静态字段仅按顺序初始化一次(所以

public class DatabaseQueries {
  private static Map<DatabaseActions, String> database = new HashMap<>();
  public static Map<DatabaseActions, String> getDatabase() {
    return Collections.unmodifiableMap(database);
  }
  static {
    database.put(
      DatabaseActions.LIST_CHIHUAHUA,
      "SELECT id, nome, immagine FROM chihuahua ORDER BY data_nascita DESC"
    );
    // more...
  }
}

上面的代码线程安全吗?即使我有一个静态块和一个单独的地图静态初始化!


我在这个答案中找到了:

静态类初始化由 Java 保证是线程安全的。

上面我有一个静态块和一个静态变量,有什么区别吗?两者都在类启动时初始化吗?

所以我的问题是静态块和静态变量(静态块之外(是否都能保证不会导致多线程问题

你写的

即使我有一个静态块和一个单独的地图静态初始化!

这显示了错误的心态。您没有单独的初始化。

代码

public class DatabaseQueries {
  private static Map<DatabaseActions, String> database = new HashMap<>();
  static {
    database.put(
      DatabaseActions.LIST_CHIHUAHUA,
      "SELECT id, nome, immagine FROM chihuahua ORDER BY data_nascita DESC"
    );
  }
}

与 相同

public class DatabaseQueries {
  private static Map<DatabaseActions, String> database;
  static {
    database = new HashMap<>();
    database.put(
      DatabaseActions.LIST_CHIHUAHUA,
      "SELECT id, nome, immagine FROM chihuahua ORDER BY data_nascita DESC"
    );
  }
}

static字段的所有初始值设定项(编译时常量除外(与所有static {}块合并到单个初始值设定项中,按它们在类声明中出现的顺序。

线程安全适用于完成单个生成的初始值设定项以及初始化数据的后续读取。

因此,如果在类初始化后从未修改映射,则它是线程安全的。没有必要,但强烈建议强制执行"初始化后不修改"规则:

public class DatabaseQueries {
  private static final Map<DatabaseActions, String> database;
  static { // keep variable declaration and initializer close for readability
    Map<DatabaseActions, String> map = new HashMap<>();// mutable ref during initialization
    map.put(
      DatabaseActions.LIST_CHIHUAHUA,
      "SELECT id, nome, immagine FROM chihuahua ORDER BY data_nascita DESC"
    );
    // more...
    database = Collections.unmodifiableMap(map);// enforce read-only
  }
  public static Map<DatabaseActions, String> getDatabase() {
    return database;// no wrapping necessary, it’s already wrapped
  }
}

一旦初始化/执行所有静态成员,该类就被视为"已初始化"。在您的情况下,这是databasestatic {}.这是单线程完成的,在其他任何东西可以访问新初始化的类之前。

之后,Java 内存模型保证,尽管缺乏显式同步,但在类初始化之前完成的所有更改对之后发生的所有操作都是可见的(类似于由构造函数初始化的字段(。

这并不意味着它是线程安全的,因为database是一个可变对象。如果您指定的类是所有内容,那么仅凭这一点仍然不能保证没有外部实体可以访问database(想想反射(。

使此线程安全的最佳方法是将Collections.unmodifiableMap(...)的结果存储在静态字段中。

最新更新