如何在 Java 中创建线程安全的 ConcurrentHashSet?
在本文中,我们将了解创建线程安全的 HashSet 实例有哪些可能性,并了解什么与 HashSet 的 ConcurrentHashMap 等效。我们还将研究每种方法的优缺点。
在 JDK8 之前,我们无法创建线程安全的 ConcurrentHashMap,因为 JDK8 中的 java.util.concurrent 包不提供名为 ConcurrentHashSet 的类,因此添加了两个新方法,下面将进行讨论。ConcurrentHashMap 是 Map 实现,允许我们在迭代时修改 Map。ConcurrentHashMap 操作是线程安全的。 ConcurrentHashMap 不允许键和值为 null。
创建线程安全的 ConcurrentHashSet 的方法
当谈到 ConcurrentHashSet 时,可以使用 ConcurrentHashMap 创建,它允许使用 keySet(default Value) 和 newKeySet() 方法来获取适当的 Set。这提供了对诸如 contains()、remove() 等函数的访问。
keySet (Default Value)
newKeySet()
使用 KeySet(default Value) 创建 ConcurrentHashSet
ConcurrentHashMap 类的 keySet(default Value) 方法提供了存储在映射中的键的 Set 视图。该集合由映射本身支持,这意味着对映射的修改将反映在集合中,反之亦然。
语法
public ConcurrentHashMap.KeySetView<K,V> keySet(default_value)
参数
使用 ConcurrentHashMap 创建集合时传递 default_value
示例
//一个 Java 程序,用于演示确保线程安全的 ConcurrentHashSet 的实现。 import java.io.*; import java.util.*; public class TutorialsPoint { public static void main (String[] args) { ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>(); map.put("Tutorials",1); map.put("Point",2); map.put("Java", 3); map.put("article on",4); map.put("Threads", 5); //使用concurrenthashmap创建一个Set Set intialSet = map.keySet(120); //值120是任意默认值 System.out.println("初始集合:" + intialSet); //该值将保持不变为120 //但不会遇到任何错误。 intialSet.add("Element"); System.out.println("删除元素后" + intialSet); //检查集合是否包含,如果是,我们将删除并打印集合 if(intialSet.contains("Threads")) { intialSet.remove("Threads"); System.out.println("删除元素后 " + intialSet); } } }
输出
初始集合:[Threads, Java, Tutorials, Point, article on] 删除元素后 [Threads, Java, Tutorials, Element, Point, article on] 删除元素后 [Java, Tutorials, Element, Point, article on]
缺点
尝试向集合添加新元素时,值将保持不变,即我们在创建集合时设置为默认值的值。
由于我们面临的所有限制,这就是为什么他们引入了 newKeySet() 方法,该方法返回由 ConcurrentHashMap 支持的集合,其中与键关联的值的类型为 Boolean。
如何使用 NewKeySet() 创建 ConcurrentHashSet
newKeySet() 方法属于 ConcurrentHashMap 类,生成一个由 ConcurrentHashMap 支持的新集合,其中指定类型的元素映射到 Boolean.TRUE。
语法
public static <K> ConcurrentHashMap.KeySetView<K,Boolean> newKeySet()
下面的代码将让我们了解如何使用 newKeySet() 创建 ConcurrentHashSet。为了解决我们在第一种方法中遇到的所有问题,我们现在将使用 newkeySet()。
示例
// 一个 Java 程序,用于演示确保线程安全的 ConcurrentHashSet 的实现。 import java.io.*; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class TutorialsPoint { public static void main (String[] args) { // 创建映射 ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>(); map.put("Tutorials",1); map.put("Point",2); map.put("Java", 3); map.put("article on",4); map.put("Threads", 5); Set <String> subjectSet = ConcurrentHashMap.newKeySet(); subjectSet.add("Computer Networks"); subjectSet.add("DBMS"); subjectSet.add("OS"); // 显示集合的值 System.out.println("在将元素添加到主题集合之前:" + subjectSet); //使用 add() 方法将元素插入集合 subjectSet.add("DSA"); // 再次打印集合以验证更改 System.out.println("在将元素添加到主题集合之后:" + subjectSet); // 我们可以使用 contains() 方法来检查集合中元素的存在 if(subjectsSet.contains("DSA")) System.out.println("是的,它在那里"); else System.out.println("不,它不在那里"); // 我们可以直接这样做:subjectsSet.contains("DSA")); 它返回布尔值 // 要从集合中删除元素,请使用 remove() 方法 subjectSet.remove("OS"); System.out.println("从主题集合中删除元素后:" + subjectSet); } }
输出
在将元素添加到主题集之前: [Computer Networks, OS, DBMS] 将元素添加到主题集后: [Computer Networks, DSA, OS, DBMS] 是的,它在那里 从主题集中删除一个元素后: [Computer Networks, DSA, DBMS]
我们也有其他方法来创建线程安全的 HashSet,但不是经常使用,而且效率比我们上面讨论的方法要低。
使用 Collections 实用程序类的 ConcurrentHashSet
属于 Java Collections 类的 synchronizedSet() 方法用于获取同步(线程安全)集合,该集合由指定集合支持。
在此方法中,我们利用 java.util.Collections 提供的 synchronizedSet() 方法来创建线程安全的 HashSet 实例。
语法
public static <T> Set<T> synchronizedSet(Set<T> s)
参数
此集合封装在同步集合中。
示例
import java.io.*; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.Collections; import java.util.HashSet; public class TutorialsPoint { public static void main (String[] args) { Set<String> names= new HashSet<String>(); //让我们使用 add() 向集合添加值 names.add("Hari"); names.add("Revanth"); names.add("Lokesh"); names.add("Kiran"); //创建同步集合 Set<String> synchronizedSet = Collections.synchronizedSet(names); System.out.println("同步集合是:"+synchronizedSet ); } }
输出
同步集合是 :[Revanth, Hari, Kiran, Lokesh]
它效率低于上面讨论的那些。与实现低级并发机制的 ConcurrentHashMap 相比,基本上synchronizedSet()将 Set 包装到 synchronized 装饰器中。
使用 CopyOnWriteArraySet 实现线程安全设置
用于创建线程安全实现的最后一种方法是 CopyOnWriteArraySet。以下是创建 Set 实例的代码片段。
示例
Set<String> copyOnArraySet = new CopyOnWriteArraySet<>(); copyOnArraySet.add("sample");
以下是我们需要考虑的一些重要属性和缺点 -
它使用数组而不是 HashMap 来存储数据,这意味着与 ConcurrentHashMap 相比,contains() 或 remove() 等操作的复杂度为 O(n),而 ConcurrentHashMap 的复杂度为 O(1)。
它适用于非常小的应用程序,我们需要防止遍历期间线程之间的干扰。
Raster 不提供擦除变量的操作。
结论
在本文中,我们看到了创建线程安全 Set 实例的各种可能性。最初,我们探索了在 Java 中创建由 ConcurrentHashMap 支持的 ConcurrentHashSet。以及这些方法之间的差异,当需要线程安全的哈希集时,这应该是首选。
最后,我们还讨论了synchronizedSet()、CopyOnWriteArraySet方法及其性能缺陷。