15

显然,有两种方法可以使用 Java 的实用程序类来获取线程安全的 HashSet实例。Collections

我问:

  • 它们有何不同?
  • 哪个,在什么情况下,比另一个更受欢迎?
4

5 回答 5

28

你可能想到的是

Set<Type> set = Collections.newSetFromMap(new ConcurrentHashMap<Type, Boolean>());

这支持并发更新和读取。它的迭代器不会抛出 ConcurrentModicationException。然而

Set<Type> set = Collections.synchronizedSet(new HashSet<Type());

重量更轻,但一次只允许一个线程访问集合。如果要对其进行迭代,则需要显式锁定该集合,如果您不以安全的方式更新它(在迭代时),您仍然可以获得 CME

于 2012-07-05T19:04:03.923 回答
9

第一个返回一个 Set 基本上具有与作为参数传递的映射相同的线程安全和性能保证。如果地图不是线程安全的,那么集合也不会是线程安全的。您通常使用此方法从并发映射创建并发集,因为 API 中没有 ConcurrentHashSet。

第二个返回给定集合的代理,它的所有方法都是同步的。

于 2012-07-05T18:59:00.717 回答
3

实际上你可能会得到几个 Set 线程安全的实现。

一、Collections.synchronizedSet(new HashSet

我不会推荐这个解决方案。它本身是线程安全的,在并发环境中仍然必须小心使用。看:

Stack stack = new SynchronizedArrayStack(new ArrayStack());
...
// don't do this in a multi-threaded environment
if (!stack.isEmpty()) {
  stack.pop();              // can throw IllegalStateException
}

因此,您必须使用客户端锁定:

synchronized(stack) {
  if (!stack.isEmpty()) {
    stack.pop();
  }
}

二、Set 接口的第二种可选并发实现 - CopyOnWriteArraySet。但是,此解决方案不应在您期望进行多次搜索或插入的环境中使用。但是每个元素的迭代成本 O(1) 比 HashSet 快,并且它有一个优势,在某些应用程序中非常引人注目。

三、最后一个使用CuncurrentHashMap的实现:

Collections.newSetFromMap(new ConcurrentHashMap<Type, Boolean>());

它使用 java.util.concurrent.locks.Lock 的实现。该映射将自身划分为可以单独锁定的部分,从而提高了并发性。因此,您应该在最后两个选项之间进行选择。

Thera 也是有序集合实现的选项。我建议你阅读Java Generics and Collections 章节11.5 Collections and Thread Safety

于 2012-07-11T10:32:05.737 回答
1

Collections API 将地图的构建留给客户端,如下所示:

ConcurrentHashMap<String, Boolean> conMap = new   ConcurrentHashMap<String, Boolean>();
Set<String> users = Collections.newSetFromMap(conMap);
System.out.println("Users: " + users);
users.add("Jon");//results in adding to the conMap instance
users.add("Tyron");
System.out.println("Users: " + users);
System.out.println("conMap = " + conMap);

添加到集合也添加到地图

用户:[Tyron, Jon] conMap = {Tyron=true, Jon=true}

conMap.put("Jubin", Boolean.FALSE);
System.out.println("Users: " + users);
System.out.println("conMap = " + conMap);

添加到地图也会导致添加到集合

用户:[Tyron, Jubin, Jon] conMap = {Tyron=true, Jubin=false, Jon=true}

ConcurrentHashMap.newKeySet 使用 KeySetView 创建新的 HashMap

ConcurrentHashMap.KeySetView<String, Boolean> keySetView = ConcurrentHashMap.newKeySet();
keySetView.add("Feba");
System.out.println("keySetView = " + keySetView);
System.out.println("keySetView.getMap() = " + keySetView.getMap());

keySetView = [Feba] keySetView.getMap() = {Feba=true}

keySetView.getMap().put("BeN",Boolean.TRUE);
System.out.println("keySetView = " + keySetView);
System.out.println("keySetView.getMap() = " + keySetView.getMap());

keySetView = [BeN, Feba] keySetView.getMap() = {BeN=true, Feba=true}

于 2017-05-07T12:51:04.847 回答
-2

你误会了。newSetFromMap() 不提供线程安全。再次检查 Javadoc。

还有一个小问题就是这两种方法的输入类型不同。

于 2012-07-05T18:58:51.423 回答