📜  ConcurrentHashMap 如何在Java中实现线程安全?

📅  最后修改于: 2022-05-13 01:55:49.185000             🧑  作者: Mango

ConcurrentHashMap 如何在Java中实现线程安全?

ConcurrentHashMap是一个 支持检索的完全并发和更新的高预期并发的哈希表。此类遵循与 Hashtable 相同的功能规范,并包含 Hashtable 的所有方法。 ConcurrentHashMap 在Java.util.Concurrent 包中。

句法:

public class ConcurrentHashMap
extends AbstractMap
implements ConcurrentMap, Serializable

其中 K 指的是这个 map 维护的键的类型,V 指的是映射值的类型

ConcurrentHashmap的需求:

  • 虽然 HashMap 有很多优点,但它不能用于多线程,因为它不是线程安全的。
  • 尽管 Hashtable 被认为是线程安全的,但它也有一些缺点。例如,Hashtable 需要锁定才能读取打开,即使它不影响对象。
  • n HashMap,如果一个线程正在迭代一个对象,另一个线程试图访问同一个对象,它会抛出 ConcurrentModificationException,而并发 hashmap 不会抛出 ConcurrentModificationException。

如何使 ConcurrentHashMap 线程安全成为可能?

  • Java.util.Concurrent.ConcurrentHashMap 类通过将映射划分为段来实现线程安全,锁不是对整个对象而是对一个段,即一个线程需要一个段的锁。
  • 在 ConcurrenHashap 中,读操作不需要任何锁。

示例 1:

Java
// Java Program to llustarte ConcurrentModificationException
// Using Normal Collections
 
// Importing required classes
import java.util.*;
import java.util.concurrent.*;
 
// Main class extending Thread class
class GFG extends Thread {
 
    // Creating a static HashMap class object
    static HashMap m = new HashMap();
 
    // run() method for the thread
    public void run()
    {
 
        // Try block to check for exceptions
        try {
 
            // Making thread to sleep for 3 seconds
            Thread.sleep(2000);
        }
 
        // Catch block to handle exceptions
        catch (InterruptedException e) {
        }
 
        // Display message
        System.out.println("Child Thread updating Map");
 
        // Putting element in map
        m.put(103, "C");
    }
 
    // Method 2
    // Main driver method
    public static void main(String arg[])
        throws InterruptedException
    {
 
        // Adding elements to map object created above
        // using put() method
        m.put(101, "A");
        m.put(102, "B");
 
        // Creating thread inside main() method
        GFG t = new GFG();
 
        // Starting the thread
        t.start();
 
        // Operating keySet() method and
        // storing it in Set class object
        Set s1 = m.keySet();
 
        // Iterating over Set class object
        // using iterators
        Iterator itr = s1.iterator();
 
        // Holds true till there is single element present
        // inside object
        while (itr.hasNext()) {
 
            // traversing over elements in object
            // using next() method
            Integer I1 = (Integer)itr.next();
 
            // Print statement
            System.out.println(
                "Main Thread Iterating Map and Current Entry is:"
                + I1 + "..." + m.get(I1));
 
            // Making thread to sleep for 3 seconds
            Thread.sleep(3000);
        }
 
        // Printing all elements on console
        System.out.println(m);
    }
}


Java
// Java Program to llustarte ConcurrentModificationException
// Using ConcurrentHashMap
 
// Importing required classes
import java.util.*;
import java.util.concurrent.*;
 
// Main class extending Thread class
class Main extends Thread {
 
    // Creating static concurrentHashMap object
    static ConcurrentHashMap m
        = new ConcurrentHashMap();
 
    // Method 1
    // run() method for the thread
    public void run()
    {
 
        // Try block to check for exceptions
        try {
 
            // Making thread to sleep for 2 seconds
            Thread.sleep(2000);
        }
 
        // Catch block to handle the exceptions
        catch (InterruptedException e) {
        }
 
        // Display message
        System.out.println("Child Thread updating Map");
 
        // Inserting element
        m.put(103, "C");
    }
 
    // Method 2
    // Main driver method
    public static void main(String arg[])
        throws InterruptedException
    {
 
        // Adding elements to object created of Map
        m.put(101, "A");
        m.put(102, "B");
 
        // Creating thread inside main() method
        Main t = new Main();
 
        // Starting thread
        t.start();
 
        // Creating object of Set class
        Set s1 = m.keySet();
 
        // Creating iterator for traversal
        Iterator itr = s1.iterator();
 
        // Condition holds true till there is single element
        // in Set object
        while (itr.hasNext()) {
 
            // Iterating over elements
            // using next() method
            Integer I1 = itr.next();
 
            // Display message
            System.out.println(
                "Main Thread Iterating Map and Current Entry is:"
                + I1 + "..." + m.get(I1));
 
            // Making thread to sleep for 3 seconds
            Thread.sleep(3000);
        }
 
        // Display elements of map objects
        System.out.println(m);
    }
}


输出:

Main Thread Iterating Map and Current Entry is:101...A
Child Thread updating Map
Exception in thread "main" java.util.ConcurrentModificationException
       at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1493)
       at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1516)
       at Main.main(Main.java:30)

输出说明:

上述程序中使用的类扩展了 Thread 类。让我们看看控制流。所以, 最初,上述Java程序包含一个线程。当我们遇到语句 Main t= new Main() 时,我们正在为扩展 Thread 类的类创建一个对象。所以,每当我们调用 t.start() 方法时,子线程就会被激活并调用 run() 方法.现在主线程开始执行,每当子线程更新同一个地图对象时,都会抛出一个名为 ConcurrentModificationException 的异常。

现在让我们使用 ConcurrentHashMap 修改上述程序,以解决上述程序执行时产生的上述异常。

示例 2:

Java

// Java Program to llustarte ConcurrentModificationException
// Using ConcurrentHashMap
 
// Importing required classes
import java.util.*;
import java.util.concurrent.*;
 
// Main class extending Thread class
class Main extends Thread {
 
    // Creating static concurrentHashMap object
    static ConcurrentHashMap m
        = new ConcurrentHashMap();
 
    // Method 1
    // run() method for the thread
    public void run()
    {
 
        // Try block to check for exceptions
        try {
 
            // Making thread to sleep for 2 seconds
            Thread.sleep(2000);
        }
 
        // Catch block to handle the exceptions
        catch (InterruptedException e) {
        }
 
        // Display message
        System.out.println("Child Thread updating Map");
 
        // Inserting element
        m.put(103, "C");
    }
 
    // Method 2
    // Main driver method
    public static void main(String arg[])
        throws InterruptedException
    {
 
        // Adding elements to object created of Map
        m.put(101, "A");
        m.put(102, "B");
 
        // Creating thread inside main() method
        Main t = new Main();
 
        // Starting thread
        t.start();
 
        // Creating object of Set class
        Set s1 = m.keySet();
 
        // Creating iterator for traversal
        Iterator itr = s1.iterator();
 
        // Condition holds true till there is single element
        // in Set object
        while (itr.hasNext()) {
 
            // Iterating over elements
            // using next() method
            Integer I1 = itr.next();
 
            // Display message
            System.out.println(
                "Main Thread Iterating Map and Current Entry is:"
                + I1 + "..." + m.get(I1));
 
            // Making thread to sleep for 3 seconds
            Thread.sleep(3000);
        }
 
        // Display elements of map objects
        System.out.println(m);
    }
}


输出
Main Thread Iterating Map and Current Entry is:101...A
Child Thread updating Map
Main Thread Iterating Map and Current Entry is:102...B
Main Thread Iterating Map and Current Entry is:103...C
{101=A, 102=B, 103=C}

输出说明:

上述程序中使用的 Class 扩展了 Thread 类。让我们看看控制流,因为我们知道在 ConcurrentHashMap 中,当一个线程正在迭代时,剩余的线程被允许以安全的方式执行任何修改。在上面的程序中,主线程正在更新 Map,同时子线程也在尝试更新 Map 对象。此程序不会抛出 ConcurrentModificationException。

Hashtable、Hashmap、ConcurrentHashmap的区别

HashTable

HashMap

ConcurrentHashMap

We will get Thread-safety by locking whole map object.It is not Thread-safe.We will get Thread-safety without locking Total Map object just with segment level lock.
Every read and write operation requires an objectstotal map object lock.It requires no lock.Read operations can be performed without lock but write operations can be performed with segment level lock.
At a time only one thread is allowed to operate on map(Synchronized)At a time multiple threads are not allowed to operate. It will throw an exceptionAt  a time multiple threads are allowed to operate on map objects in a safe manner
While one thread iterates Map object, the other Threads are not allowed to modify the map otherwise we get ConcurrentModificationExceptionWhile one thread iterates Map object, the other Threads are not allowed to modify the map otherwise we get ConcurrentModificationExceptionWhile one thread iterates Map object, the other Threads are allowed to modify the map and we won’t get ConcurrentModificationException
Null is not allowed for both keys and valuesHashMap allows one null key and multiple null valuesNull is not allowed for both keys and values.
Introduced in 1.0 versionIntroduced in 1.2 versionIntroduced in 1.5 version