ConcurrentHashMap是Java中线程安全且高性能的哈希表实现,适用于多线程环境下高效操作键值对。它通过CAS操作和synchronized锁节点实现高并发读写,避免了HashTable的全局锁性能瓶颈。与HashMap相比,它支持并发修改而不抛出异常;与HashTable相比,其分段锁或节点级锁机制显著提升并发性能。在Java 8中,底层采用Node数组+链表/红黑树结构,put操作先CAS插入再必要时加锁,get操作无锁但保证可见性。推荐在多线程共享数据场景使用,如缓存、计数器等。注意其不允许null键或值,迭代器为弱一致性,复合操作应使用compute、merge等原子方法以避免竞态条件。合理设置初始容量可减少扩容开销,同时需关注键的hashCode均匀性及内存占用问题。

ConcurrentHashMap是Java并发编程中不可或缺的利器,它提供了一种线程安全且高性能的哈希表实现。简单来说,当你需要在多线程环境下安全、高效地操作一个键值对集合时,ConcurrentHashMap往往是你的首选,因为它在保证数据一致性的同时,最大程度地提升了并发性能,避免了传统HashTable或Collections.synchronizedMap()带来的性能瓶颈。
使用ConcurrentHashMap非常直接,它提供了与HashMap类似的API,但在内部处理了所有的并发细节。
首先,你需要创建一个ConcurrentHashMap实例。通常,我们不需要指定初始容量,但在处理大量数据时,预估一个合理的初始容量(或使用
new ConcurrentHashMap<>(initialCapacity)
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ConcurrentHashMapDemo {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, Integer> userScores = new ConcurrentHashMap<>();
// 基本的put操作
userScores.put("Alice", 100);
userScores.put("Bob", 95);
System.out.println("初始分数: " + userScores); // 输出: 初始分数: {Alice=100, Bob=95}
// 基本的get操作
Integer aliceScore = userScores.get("Alice");
System.out.println("Alice的分数: " + aliceScore); // 输出: Alice的分数: 100
// 基本的remove操作
userScores.remove("Bob");
System.out.println("移除Bob后: " + userScores); // 输出: 移除Bob后: {Alice=100}
// putIfAbsent: 如果key不存在,则放入;如果存在,则不操作并返回旧值
userScores.putIfAbsent("Alice", 110); // Alice已存在,不会更新
userScores.putIfAbsent("Charlie", 88); // Charlie不存在,会放入
System.out.println("使用putIfAbsent后: " + userScores); // 输出: 使用putIfAbsent后: {Alice=100, Charlie=88}
// compute: 原子地计算并更新一个值
// 假设我们要给Alice的分数加10
userScores.compute("Alice", (key, oldValue) -> oldValue == null ? 0 : oldValue + 10);
System.out.println("Alice分数更新后: " + userScores.get("Alice")); // 输出: Alice分数更新后: 110
// merge: 如果key存在,则使用remappingFunction合并旧值和新值;如果key不存在,则放入新值
userScores.merge("Alice", 5, (oldValue, newValue) -> oldValue + newValue); // 110 + 5 = 115
userScores.merge("David", 70, (oldValue, newValue) -> oldValue + newValue); // David不存在,直接放入70
System.out.println("使用merge后: " + userScores); // 输出: 使用merge后: {Alice=115, Charlie=88, David=70}
// 遍历ConcurrentHashMap
// 注意:迭代器是弱一致性的,它反映的是在迭代器创建时或创建后某个时刻的映射状态,不保证实时性。
// 但它不会抛出ConcurrentModificationException。
System.out.println("遍历ConcurrentHashMap:");
userScores.forEach((user, score) -> System.out.println(user + ": " + score));
// 模拟多线程并发操作
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 100; i++) {
final String user = "User" + (i % 10); // 10个用户
executor.submit(() -> {
userScores.compute(user, (k, v) -> v == null ? 1 : v + 1);
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("并发更新后: " + userScores);
// 理论上每个用户的值都应该是10,因为有100次操作,10个用户,每个用户被操作了10次。
// 验证:userScores.get("User0") 应该等于10
}
}在实际应用中,
putIfAbsent
compute
merge
compute
get
put
立即学习“Java免费学习笔记(深入)”;
这个问题,我个人觉得是理解ConcurrentHashMap价值的关键。我们先从源头说起:
HashMap
HashMap
HashTable
synchronized
HashTable
put
get
put
HashTable
ConcurrentHashMap
HashTable
Segment
Segment
ReentrantLock
Segment
Segment
CAS
Node
synchronized
ConcurrentHashMap
HashMap
何时选择ConcurrentHashMap?
我的经验是,只要你的应用是多线程的,并且你需要一个共享的、可变的数据结构来存储键值对,那么几乎总是应该考虑
ConcurrentHashMap
compute
merge
如果你确定是单线程环境,或者只是作为局部变量使用,那
HashMap
HashTable
ConcurrentHashMap
启科网络商城系统由启科网络技术开发团队完全自主开发,使用国内最流行高效的PHP程序语言,并用小巧的MySql作为数据库服务器,并且使用Smarty引擎来分离网站程序与前端设计代码,让建立的网站可以自由制作个性化的页面。 系统使用标签作为数据调用格式,网站前台开发人员只要简单学习系统标签功能和使用方法,将标签设置在制作的HTML模板中进行对网站数据、内容、信息等的调用,即可建设出美观、个性的网站。
0
理解ConcurrentHashMap的内部机制,能让我们更好地驾驭它,甚至在遇到一些“奇特”行为时能有所预判。Java 8的ConcurrentHashMap实现与Java 7及之前版本有显著不同,放弃了
Segment
synchronized
简单来说,它的底层是一个
Node<K,V>[] table
初始化与扩容:
table
put
resize
table
HashMap
ForwardingNode
put
table
ConcurrentHashMap
CAS
Unsafe.compareAndSwapObject
Node
ConcurrentHashMap
ForwardingNode
synchronized
Node
table
put
size
size
get
get
table
Node
value
volatile
get
ConcurrentHashMap
volatile
CAS
ConcurrentHashMap
volatile
CAS
CAS
synchronized
总结一下,Java 8的ConcurrentHashMap通过
CAS
synchronized
Node
尽管ConcurrentHashMap功能强大且性能卓越,但在实际使用中,仍然有一些点需要注意,否则可能会遇到一些意料之外的行为或性能问题。
复合操作的非原子性: 虽然
ConcurrentHashMap
put
get
remove
get
put
// 假设多个线程同时执行这段代码,期望每次都递增1
Integer value = map.get("counter");
if (value == null) {
map.put("counter", 1);
} else {
map.put("counter", value + 1); // 这里可能出现问题,两个线程同时get到旧值,导致只递增了一次
}解决方案: 使用
putIfAbsent
compute
merge
// 正确的递增方式
map.compute("counter", (key, oldValue) -> oldValue == null ? 1 : oldValue + 1);不允许null键或null值:
ConcurrentHashMap
HashTable
null
get(key)
null
null
put(null, value)
put(key, null)
NullPointerException
null
Optional
迭代器的弱一致性:
ConcurrentHashMap
ConcurrentModificationException
ConcurrentHashMap
new ArrayList<>(map.entrySet())
初始容量与负载因子: 虽然
ConcurrentHashMap
initialCapacity
ConcurrentHashMap
// 假设你预计会有大约1000个元素 ConcurrentHashMap<String, Data> myCache = new ConcurrentHashMap<>(1500); // 1500 * 0.75 约等于 1125
键的哈希性能:
ConcurrentHashMap
hashCode()
equals()
hashCode()
equals()
内存占用:
ConcurrentHashMap
Node
Entry
HashMap
HashMap
理解这些点,可以帮助我们更自信、更高效地在项目中运用ConcurrentHashMap。它是一个强大的工具,但任何工具都有其最佳使用场景和需要注意的细节。
以上就是Java中ConcurrentHashMap的使用方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号