繁体   English   中英

Java static 阻塞线程安全

[英]Java static block thread safety

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

由于 JLS 保证 class 初始化阶段是顺序的,即非并发的,因此不需要进一步同步...

好的,所以我知道 static 字段仅按顺序初始化一次(所以

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...

  }

}

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


我在这个答案中找到了:

Static class 初始化由 Java 保证是线程安全的。

上面我有一个 static 块和一个 static 变量,有什么区别吗? 是否都在 class 启动时初始化?

所以我的问题是,如果 static 块和 static 变量(在 static 块之外)都保证不会导致多线程问题

你写了

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

这表明了一种错误的心态。 您没有单独的初始化。

编码

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 {}块合并到单个初始化程序中,按照它们在 ZA2F2ED4F8EBC2CBB4C21A21DZ 声明中出现的顺序。

线程安全适用于单个结果初始化程序的完成和初始化数据的后续读取。

因此,如果在 class 初始化之后从未修改过 map,则它是线程安全的。 没有必要但强烈建议强制执行“初始化后不修改”规则:

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
  }
}

一旦所有 static 成员都被初始化/执行,class 被认为是“初始化”。 在您的情况下,这是databasestatic {} 这是单线程完成的,在其他任何东西都可以访问新初始化的 class 之前。

After that, the Java Memory Model guarantees that, despite lack of explicit synchronization, all changes finished before the class is initialized are visible to all operations that happen afterwards (similar with fields initialized by a constructor).

这并不意味着它是线程安全的,因为database是可变的 object。 如果您指定的 class 就是一切,那么仅此一项仍然不能保证没有外部实体可以访问database (想想反射)。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM