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 exception | At 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 ConcurrentModificationException | While one thread iterates Map object, the other Threads are not allowed to modify the map otherwise we get ConcurrentModificationException | While 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 values | HashMap allows one null key and multiple null values | Null is not allowed for both keys and values. |
Introduced in 1.0 version | Introduced in 1.2 version | Introduced in 1.5 version |