是Java HashSet线程安全的只读?

Asa*_*ika 27 java concurrency hashset

如果我在通过Collections.unmodifiableSet()运行它后有一个HashSet实例,它是否是线程安全的?

我问这个,因为Set文档声明它不是,但我只是执行读操作.

Bri*_*ach 41

来自Javadoc:

请注意,此实现不同步.如果多个线程同时访问哈希集,并且至少有一个线程修改了该集,则必须在外部进行同步

阅读不会修改一套,所以你很好.

  • 除了创建 HashSet 并向其添加值,因此它不是空的,写入它很有用。所以这个答案是误导性的。 (2认同)

Mar*_*ers 13

HashSet如果以只读方式使用,将是线程安全的.这并不意味着您传递给的任何 Set Collections.unmodifiableSet()都将是线程安全的.

想象一下这个天真的实现contains缓存最后检查的值:

Object lastKey;
boolean lastContains;

public boolean contains(Object key) {
   if ( key == lastKey ) {
      return lastContains;
   } else {
      lastKey = key;
      lastContains = doContains(key);
      return lastContains;
   }
}
Run Code Online (Sandbox Code Playgroud)

显然,这不是线程安全的.


axt*_*avt 11

这将是线程安全的,但仅仅是因为Collections.unmodifiableSet()内部Set以安全的方式(通过final字段)发布目标.

请注意,通常诸如"只读对象始终是线程安全的"之类的语句是不正确的,因为它们没有考虑操作重新排序的可能性.

(理论上)可能的是,由于操作重新排序,在完全初始化对象并填充数据之前,对该只读对象的引用将对其他线程可见.为了消除这种可能性,您需要以安全的方式发布对对象的引用,例如,通过将它们存储在final字段中,就像它一样Collections.unmodifiableSet().


jmg*_*jmg 6

如果不改变它,每个数据结构都是线程安全的.

因为你必须改变HashSet才能初始化它,所以必须在初始化集合的线程和所有读取线程之间进行一次同步.你所要做的仅仅是一个时间.例如,当您将对不可修改集的引用传递给之前从未触及它的新线程时.

  • **那是不正确的**.至少在Java中.你不变异的对象和**不可变**对象之间有区别.这两个只有不可变对象自动线程安全.如果出现以下情况,对象是不可变的:1)在构造之后它不能被修改2)它的所有字段都是最终的3)这个引用在构造期间没有被转移 (9认同)
  • 这不完全正确.看看我的答案,举个不公平的例子.仍有变异,但用户无法知道; 它是封装状态的突变. (4认同)