如何在 Java 中创建线程安全的 ConcurrentHashSet?

javaobject oriented programmingprogramming

在本文中,我们将了解创建线程安全的 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方法及其性能缺陷。


相关文章