ConcurrentHashMap

ConcurrentHashMap

ConcurrentHashMap 是 Java 中的一个线程安全的哈希表,它允许多个线程同时读写而不需要额外的同步开销。它通过使用分段锁(segment locks)来实现对哈希表的并发访问,从而提高了并发性能。

ConcurrentHashMap基本用法

  1. 创建 ConcurrentHashMap
    你可以指定初始容量和负载因子来创建一个 ConcurrentHashMap,也可以使用默认值。

    ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
    
  2. 添加元素
    使用 putIfAbsent 方法可以安全地添加元素,如果键不存在,则添加;如果已存在,则返回已存在的值。

    String value = map.putIfAbsent(key, value);
    
  3. 读取元素
    直接通过键来获取值。

    String value = map.get(key);
    
  4. 更新元素
    使用 computeIfPresent 方法可以原子地更新键对应的值。

    map.computeIfPresent(key, (k, v) -> newValue);
    
  5. 删除元素
    使用 remove 方法可以删除指定的键值对。

    String value = map.remove(key);
    
  6. 遍历
    ConcurrentHashMap 允许在迭代过程中进行元素的添加、删除和更新操作。

    for (Map.Entry<String, String> entry : map.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue();
        // 进行操作
    }
    
  7. 原子操作
    ConcurrentHashMap 提供了多种原子操作,如 putIfAbsentcomputeIfAbsentcomputemerge 等。

  8. 大小和键值集合
    可以获取 ConcurrentHashMap 的大小,键集合和值集合。

    int size = map.size();
    Set<String> keys = map.keySet();
    Collection<String> values = map.values();
    
  9. 清空
    使用 clear 方法可以清空整个 ConcurrentHashMap

    map.clear();
    
  10. 并发级别
    ConcurrentHashMap 的构造函数允许你指定一个并发级别,这个级别决定了内部锁的数量,从而影响并发性能。

    ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(16);
    

使用 ConcurrentHashMap 时,你不需要手动同步代码块,因为它的内部操作都是线程安全的。然而,如果你需要对多个操作进行复合操作的原子性保证,你可能需要使用 Collections.synchronizedMap 或者其他同步机制。

例子

这里是一个使用 ConcurrentHashMap 的具体例子,展示了如何在多线程环境下安全地更新和访问一个共享的映射(Map)。

假设我们有一个在线购物车系统,需要记录不同用户的购物车信息,并且允许多个用户同时修改他们的购物车。

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ShoppingCartExample {
    // 使用 ConcurrentHashMap 存储用户的购物车信息
    private static ConcurrentHashMap<String, Integer> shoppingCarts = new ConcurrentHashMap<>();

    // 模拟添加商品到购物车
    public static void addToCart(String userId, int quantity) {
        // 使用 computeIfAbsent 确保即使多个线程同时访问,也只会为 userId 创建一次购物车
        shoppingCarts.computeIfAbsent(userId, k -> 0); // 默认数量为 0

        // 使用 accumulate 方法原子地增加商品数量
        for (int i = 0; i < quantity; i++) {
            shoppingCarts.accumulateAndGet(userId, 1, Integer::sum);
        }
    }

    // 模拟获取用户的购物车商品数量
    public static int getCartSize(String userId) {
        return shoppingCarts.getOrDefault(userId, 0);
    }

    public static void main(String[] args) {
        // 创建一个固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        // 模拟 5 个用户同时添加商品到购物车
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            executorService.submit(() -> {
                for (int j = 0; j < 10; j++) {
                    addToCart("user" + finalI, 1); // 用户 user0 到 user4 各添加 10 个商品
                }
            });
        }

        // 等待所有任务完成
        executorService.shutdown();

        // 验证购物车大小
        try {
            if (executorService.awaitTermination(1, TimeUnit.MINUTES)) {
                for (int i = 0; i < 5; i++) {
                    System.out.println("User " + i + " cart size: " + getCartSize("user" + i));
                }
            } else {
                System.out.println("Tasks did not complete in time.");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中:

  • 使用了 ConcurrentHashMap 来存储每个用户的购物车商品数量。
  • addToCart 方法通过 computeIfAbsent 确保每个用户都有一个初始数量为 0 的购物车,然后通过 accumulateAndGet 原子地增加商品数量。
  • getCartSize 方法返回特定用户购物车的商品数量。
  • main 方法中,创建了一个固定大小的线程池,并模拟了 5 个用户同时向他们的购物车添加商品的场景。
  • 使用了 executorService.shutdown() 来关闭线程池,并等待所有任务完成。
  • 最后,输出每个用户的购物车大小,以验证 ConcurrentHashMap 是否正确地处理了并发更新。

这个例子展示了 ConcurrentHashMap 如何在多线程环境中安全地处理并发读写操作,而无需额外的同步开销。

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

InnovatorX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值