Java ConcurrentHashMap是如何实现线程安全的
ConcurrentHashMap 哈希表,支持检索的完全并发性和更新的高预期并发性。该类遵守与Hashtable相同的功能规范,包括Hashtable的所有方法。ConcurrentHashMap属于java.util.Concurrent包。
语法:
public class ConcurrentHashMap<K,V>
extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable
其中K指的是该地图所维护的键的类型,V指的是映射值的类型。
需要ConcurrentHashmap。
- 虽然HashMap有很多优点,但它不能用于多线程,因为它不是线程安全的。
- 即使Hashtable被认为是线程安全的,它也有一些缺点。例如,Hashtable需要锁来读取开放,尽管它不影响对象。
- 在HashMap中,如果一个线程正在迭代一个对象,另一个线程正试图访问同一个对象,它会抛出ConcurrentModificationException,而concurrent hashmap不会抛出ConcurrentModificationException。
如何使ConcurrentHashMap的线程安全成为可能?
- java.util.Concurrent.ConcurrentHashMap类通过将地图分成若干段来实现线程安全,锁不是针对整个对象而是针对一个段,即一个线程需要一个段的锁。
- 在ConcurrentHashap中,读操作不需要任何锁。
示例 1:
// 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);
}
}
输出:
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.HashMapHashIterator.nextNode(HashMap.java:1493)
at java.base/java.util.HashMapKeyIterator.next(HashMap.java:1516)
at Main.main(Main.java:30)
输出解释。
在上述程序中使用的类扩展了Thread类。让我们来看看控制流。所以,最初,上述java程序包含一个线程。当我们遇到Main t= new Main()语句时,我们正在为一个扩展了Thread类的类创建一个对象。因此,每当我们调用t.start()方法时,子线程就被激活并调用run()方法。现在主线程开始执行,每当子线程更新同一个地图对象时,它将抛出一个名为ConcurrentModificationException的异常。
现在让我们通过使用ConcurrentHashMap来修改上述程序,以解决执行上述程序时产生的上述异常。
示例 2:
// 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<Integer, String> m
= new ConcurrentHashMap<Integer, String>();
// 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<Integer> s1 = m.keySet();
// Creating iterator for traversal
Iterator<Integer> 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}
输出解释。
上述程序中使用的类扩展了线程类。让我们看看控制流,我们知道在ConcurrentHashMap中,当一个线程在迭代时,其余的线程被允许以安全的方式执行任何修改。在上面的程序中,主线程正在更新Map,与此同时,子线程也在试图更新Map对象。这个程序不会抛出ConcurrentModificationException。
Hashtable, Hashmap, ConcurrentHashmap之间的区别
HashTable | HashMap | ConcurrentHashmap |
---|---|---|
我们将通过锁定整个地图对象来获得线程安全。 | 它不是线程安全的。 | 我们将获得线程安全,而不需要锁定整个地图对象,只需使用段级锁。 |
每个读和写操作都需要一个objectstotal map对象锁。 | 它不需要锁。 | 读取操作可以在没有锁的情况下进行,但写入操作可以在段级锁的情况下进行。 |
每次只允许一个线程对地图进行操作(同步)。 | 在一个时间段内,不允许多个线程操作。它将抛出一个异常 | 在同一时间,允许多个线程以安全的方式对地图对象进行操作 |
当一个线程迭代地图对象时,其他线程不允许修改地图,否则我们会得到 ConcurrentModificationException。 | 当一个线程迭代地图对象时,其他线程不允许修改地图,否则我们会得到 ConcurrentModificationException。 | 当一个线程迭代地图对象时,其他线程可以修改地图,我们就不会得到ConcurrentModificationException。 |
键和值都不允许为空 | HashMap允许一个空键和多个空值 | 键和值都不允许为空。 |
在1.0版本中引入 | 在1.2版本中引入 | 在1.5版本中引入 |