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


在本文中,我们将了解创建线程安全 HashSet 实例的各种可能性,并了解与 HashSet 等效的 ConcurrentHashMap。我们还将研究每种方法的优缺点。

在 JDK8 之前,我们无法创建线程安全的 ConcurrentHashMap,因为 JDK8 中的 java.util.concurrent 包不提供名为 ConcurrentHashSet 的类,添加了两种新的方法,如下所述。ConcurrentHashMap 是允许我们在迭代时修改 Map 的 Map 实现。ConcurrentHashMap 操作是线程安全的。ConcurrentHashMap 不允许键和值为 null。

创建线程安全 ConcurrentHashSet 的方法

当谈到ConcurrentHashSet时,可以通过利用ConcurrentHashMap来创建,它允许使用keySet(defaultValue)newKeySet()方法来获取一个合适的 Set。这提供了对诸如 contains()、remove() 等函数的访问。

  • keySet (默认值)

  • newKeySet()

使用 KeySet(defaultValue) 创建 ConcurrentHashSet

ConcurrentHashMap 类的keySet(defaultValue)方法提供了存储在映射中的键的 Set 视图。该集合由映射本身支持,这意味着对映射的修改将反映在集合中,反之亦然。

语法

public ConcurrentHashMap.KeySetView<K,V> keySet(default_value)

参数

在使用 ConcurrentHashMap 创建集合时传递 default_value

示例

//A Java program to demonstrate the implementation of a ConcurrentHashSet that ensures thread safety.

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);

      //Create a Set using concurrenthashmap
      Set intialSet = map.keySet(120); //The value 120 is an arbitrary default value
      System.out.println("Initial set: " + intialSet);

      // The value will remain unchanged as 120
      // but no error will be encountered.

      intialSet.add("Element");
      System.out.println("After removing an element " + intialSet);

      //Checking if the set contains if yes we will remove and print the set

      if(intialSet.contains("Threads")) {
         intialSet.remove("Threads");
         System.out.println("After removing an element " + intialSet);
      }
   }
}

输出

Initial set: [Threads, Java, Tutorials, Point, article on]
After removing an element [Threads, Java, Tutorials, Element, Point, article on]
After removing an element [Java, Tutorials, Element, Point, article on]

缺点

在尝试向集合添加新元素时,值将保持不变,即我们在创建集合时设置的默认值。

由于我们上面遇到的所有限制,这就是他们引入newKeySet()方法的原因,该方法返回一个由 ConcurrentHashMap 支持的 Set,其中与键关联的值的类型为Boolean

如何使用 NewKeySet() 创建 ConcurrentHashSet

newKeySet()方法属于 ConcurrentHashMap 类,它生成一个由 ConcurrentHashMap 支持的新集合,其中指定类型的元素映射到 Boolean.TRUE。

语法

public static <K> ConcurrentHashMap.KeySetView<K,Boolean> newKeySet()

下面提供的代码将使我们了解如何使用 newKeySet() 创建 ConcurrentHashSet。为了解决我们在第一种方法中遇到的所有问题,我们现在将使用newkeySet()

示例

// A Java program to demonstrate the implementation of a ConcurrentHashSet that ensures thread safety.

import java.io.*;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class TutorialsPoint {
   public static void main (String[] args) {

      // Creating a map
      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> subjectsSet = ConcurrentHashMap.newKeySet();

      subjectsSet.add("Computer Networks");
      subjectsSet.add("DBMS");
      subjectsSet.add("OS");

      // displaying the values of set
      System.out.println("before adding an element into subjects set: " + subjectsSet);

      //use add() method to insert elements into the set
      subjectsSet.add("DSA");

      // Print the set again to verify the changes
      System.out.println("after adding an element into subjects set: " + subjectsSet);

      // we can use utilize contains() method to check presence of an element in a  set
      if(subjectsSet.contains("DSA"))
      System.out.println("Yes it is there");
      else
      System.out.println("No it is not there");

      // we can do it directly like this :subjectsSet.contains("DSA")); it returns boolean value

      // To remove an element from a set use remove() method
      subjectsSet.remove("OS");
      System.out.println("after removing an element from subjects set: " + subjectsSet);
   }
}

输出

before adding an element into subjects set: [Computer Networks, OS, DBMS]
after adding an element into subjects set: [Computer Networks, DSA, OS, DBMS]
Yes it is there
after removing an element from subjects set: [Computer Networks, DSA, DBMS]

我们还有其他方法可以创建线程安全的 HashSet,但大多数情况下不使用,而且效率也低于我们上面讨论的方法。

使用 Collections 工具类创建 ConcurrentHashSet

synchronizedSet()方法属于 Java Collections 类,用于获取一个由指定集合支持的同步(线程安全)集合。

在此方法中,我们利用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>();
      //let’s Add values to the set using add()
      names.add("Hari");
      names.add("Revanth");
      names.add("Lokesh");
      names.add("Kiran");
      //Creating a synchronized set
      Set<String> synchronizedSet = Collections.synchronizedSet(names);
      System.out.println("Synchronized set is :"+synchronizedSet );
   }
}

输出

Synchronized set is :[Revanth, Hari, Kiran, Lokesh]

它比上面讨论的方法效率低。基本上,与实现低级并发机制的 ConcurrentHashMap 相比,synchronizedSet()将 Set 包装到同步装饰器中。

使用 CopyOnWriteArraySet 创建线程安全 Set

用于创建线程安全实现的最后一种方法是 CopyOnWriteArraySet。以下是创建 Set 实例的代码片段。

示例

Set<String> copyOnArraySet = new CopyOnWriteArraySet<>();
copyOnArraySet.add("sample");

以下是一些我们需要考虑的重要属性和缺点:

  • 它使用数组而不是 HashMap 来存储数据,这意味着与采用 O(1) 的 ConcurrentHashMap 相比,contains() 或 remove() 等操作的复杂度为 O(n)。

  • 它适用于非常小的应用程序,我们需要防止线程在遍历期间发生干扰。

  • 光栅不提供擦除变量的操作。

结论

在本文中,我们看到了创建线程安全 Set 实例的各种可能性。最初,我们探讨了在 Java 中创建ConcurrentHashSet,它由 ConcurrentHashMap 支持。以及这些方法之间的区别,当需要线程安全的哈希集时,这应该是首选方法

最后,我们还讨论了 synchronizedSet()、CopyOnWriteArraySet 方法及其性能缺点。

更新于: 2023年10月16日

543 次浏览

开启您的 职业生涯

通过完成课程获得认证

开始
广告