Java中的ConcurrentHashMap教程
并发性是现代软件开发的一个重要方面,尤其是在多线程环境中。同时管理共享数据结构需要仔细考虑,以避免竞争条件并保持数据完整性。Java 并发编程武器库中的一项强大工具是 ConcurrentHashMap。
在这篇博文中,我们将深入探讨 ConcurrentHashMap 的内容、原因、时间和方式。
什么是ConcurrentHashMap?
ConcurrentHashMap 是 Java 5 中引入的 java.util.concurrent 包中的一个类。它旨在提供对映射数据结构的线程安全访问,允许多个线程并发读写,而不需要外部同步。它是 Java 中传统“HashMap”的增强版本,针对并发操作进行了优化。
为什么使用ConcurrentHashMap?
1. 线程安全:
使用 ConcurrentHashMap 的主要原因是为了确保多线程环境中的线程安全。传统的“HashMap”实现不是线程安全的,如果没有适当的同步,并发修改可能会导致未定义的行为。ConcurrentHashMap 通过提供无需外部同步的并发访问内部机制来解决此问题。
2. 改进的性能:
与使用可能导致争用并降低性能的外部同步机制(例如“同步”块或方法)不同,“ConcurrentHashMap”采用细粒度的锁和分区。这种设计允许多个线程同时对映射的不同段进行操作,从而在高争用的场景中获得更好的性能。
3.动态可扩展性:
`ConcurrentHashMap` 是动态可扩展的。随着线程数量的增加,Map会自动调整和扩展其内部结构,以适应不断增长的线程数量,保证高效的性能,而无需人工干预。
何时使用ConcurrentHashMap?
1. 多线程环境:
当您的应用程序涉及需要同时访问和修改共享映射的多个线程时,请使用“ConcurrentHashMap”。这在 Web 应用程序、服务器端处理或任何受益于并行性的应用程序等场景中很常见。
2. 高争用:
在共享资源争用较高的情况下,“ConcurrentHashMap”可以超越传统的同步机制,提供更好的可扩展性和响应能力。
3、读写操作:
如果您的应用程序需要在映射上进行频繁的读写操作,“ConcurrentHashMap”非常适合。它允许并发读取和写入而不需要锁,从而提高吞吐量。
如何使用ConcurrentHashMap:
1.创建实例:
import java.util.concurrent.ConcurrentHashMap; |
要创建实例,可使用默认构造函数,或从多个构造函数中选择,这些构造函数允许您指定初始容量、负载系数和并发级别。
2.执行操作:
ConcurrentHashMap 提供了一组与 HashMap 类似的丰富方法。一些关键方法包括 `put()`、`get()`、`remove()`、`forEach()` 等。
concurrentMap.put("Key", 42); |
3.原子操作:
在复合操作中使用 `putIfAbsent()`、`replace()`、`compute()` 和 `merge()` 等原子方法。
concurrentMap.putIfAbsent("Key", 42); |
4.安全迭代:
在对地图进行遍历时,使用`forEach()`方法,即使在并发修改时也能确保安全遍历。
concurrentMap.forEach((key, value) -> System.out.println(key + ": " + value)); |
代码案例
提供 20 种可以使用 Java 中的 ConcurrentHashMap 的不同场景的简要说明和代码片段。
1.ConcurrentHashMap的基本用法:
确保并发读写的线程安全操作。
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); |
2. ConcurrentHashMap 中的原子更新:
无需外部同步即可执行原子更新。
map.compute("One", (key, value) -> value + 1); |
3.ConcurrentHashMap中的批量操作:
使用“forEach”来并行处理条目。
map.forEach((key, value) -> processEntry(key, value)); |
4.ConcurrentHashMap中的条件移除:
仅当满足特定条件时才删除条目。
map.remove("One", 1);
5. ConcurrentHashMap中的默认值:
为不存在的键提供默认值。
map.computeIfAbsent("Two", key -> 2);
6.ConcurrentHashMap中的合并函数:
使用自定义合并函数合并键的值。
map.merge("One", 10, Integer::sum);
7.ConcurrentHashMap中的搜索和更新:
搜索键并自动更新其值。
map.search("One", (key, value) -> { |
8.ConcurrentHashMap中的分组方式:
按特定属性对元素进行分组。
Map<Boolean, List<String>> groupedByLength = map.keySet() |
9.ConcurrentHashMap中的并行处理:
使用“forEachParallel”进行并行处理以提高性能。
map.forEachEntryParallel(2, entry -> processEntry(entry.getKey(), entry.getValue()));
10.ConcurrentHashMap中的键集操作:
对键集执行原子操作。
map.keySet().removeIf(key -> key.length() > 3);
11.ConcurrentHashMap中的条件加法:
仅当键不存在时才添加键值对。
map.putIfAbsent("Three", 3);
12.ConcurrentHashMap中的并发归约:
使用“reduceEntries”进行并发减少。
int sum = map.reduceEntries(2, (entry) -> entry.getValue(), Integer::sum);
13. 迭代ConcurrentHashMap中的值:
使用“forEachValue”迭代值。
map.forEachValue(2, value -> processValue(value));
14. 更新ConcurrentHashMap中的值:
有条件地更新值。
map.replaceAll((key, value) -> value > 5 ? value * 2 : value);
15.ConcurrentHashMap中的Key过滤:
基于谓词过滤键。
Set<String> filteredKeys = map.keySet().stream() |
16.ConcurrentHashMap中的并发枚举:
枚举条目,同时允许并发修改。
Enumeration<String> keys = map.keys(); |
17.ConcurrentHashMap中的并发搜索:
使用“searchEntries”进行并发搜索操作。
String result = map.searchEntries(2, entry -> entry.getValue() > 5 ? entry.getKey() : null);
18.ConcurrentHashMap中自定义相等性:
使用自定义“Object”作为覆盖“equals”和“hashCode”的键。
class CustomKey { |
19.ConcurrentHashMap中的线程安全初始化:
确保线程安全的延迟初始化。
map.computeIfAbsent( "LazyInit" , key -> initializeValue());
20.ConcurrentHashMap中的并发替换:
以原子方式替换键的现有值。
map.replace("One", 1, 10);
这些示例展示了“ConcurrentHashMap”在各种场景中的多功能性,从基本用法到高级并发操作。
最有用的初始化
ConcurrentHashMap最有用的是实现原子性质的初始化,可替代单例双锁初始化:
if(a=null) |
上述代码是为了保证初始化方法只执行一次,使用同步锁,如果两个以上多线程同时执行这段代码,都发现a是空,需要初始化,那么真正初始化的则只有一个线程执行,但是这种使用同步方式性能差。
使用ConcurrentHashMap的computeIfAbsent方法,只有映射中没有a值时,会执行初始化方法:
public ForumThread getThread(Long threadId) throws Exception { |
另外一种初始化应用:
public class MessageListAction extends ModelListAction { |
,, |
结论
在以并发性为主要考虑因素的世界中,"ConcurrentHashMap "作为管理共享映射数据结构的强大而高效的解决方案大放异彩。了解它的功能、优点和正确用法,对于在 Java 中构建可扩展且响应迅速的多线程应用程序至关重要。无论您是要处理高度竞争、频繁的读写操作,还是仅仅为了实现线程安全,`ConcurrentHashMap` 都是您并发编程工具包中的重要工具。