首页 > 其他分享 >ConcurrentHashMap的非线程安全使用

ConcurrentHashMap的非线程安全使用

时间:2023-10-26 23:24:58浏览次数:28  
标签:Map ConcurrentHashMap name map alice 安全 线程 id

问题

业务场景:应用会创建一个<name,id>的Map并缓存,其中key,value会被其他业务模块调用,最终数据落盘到HDFS上。

问题:发现一个奇怪的bug:id在Map中的值和业务表中的值有时候对不上,比如在业务表中查到一个id=100,但是在Map中找不到这个值。

经过分析定位,发现问题代码在这里:(大概逻辑为,如果key存在就获取value,如果不存在就+1并保存。)

Map<String, Integer> map = new ConcurrentHashMap<>();
AtomicInteger maxId = new AtomicInteger(0);

// init map and maxId ...

public int process(String name) {
    int id;
    if (map.containsKey(name)) {
        id = map.get(name);
    } else {
        id = maxId.incrementAndGet();
        map.put(name, id);
    }
    return id;
}

这里虽然使用了线程安全的ConcurrentHashMap和AtomicInteger,但是if判断条件会使线程安全失效。

举例,如果两个线程的name都是alice,依次处理,期望结果应该是Map中put了一条数据<alice,1>,然后两个线程的返回值都是1。
但是,如果两个线程同时进入if条件判断,然后都走到了else中,那么Map会put两次,最终留存下来一条数据<alice,2>,然后线程的返回值分为被id=1,id=2。

最终,id=1的这一条数据被覆盖了!

测试

使用UT可以重现这种情况:

  ExecutorService es = Executors.newFixedThreadPool(3);
  Future<Integer> f1 = es.submit(() -> module.process("alice"));
  Future<Integer> f2 = es.submit(() -> module.process("alice"));
  Future<Integer> f3 = es.submit(() -> module.process("bob"));

  logger.info(String.value0f(f1.get()));
  logger.info(String.value0f(f2.get()));
  logger.info(String.value0f(f3.get()));
  logger.info("===");
  module.getMap().forEach((k,v) -> logger.info(k+","+v));

结果如下:

1
2
3
===
alice,1
alice,2
bob,3

线程安全的话,alice对应的id应该是一样的,由此可见上述代码有漏洞。

修正

最直观的修正方法,就是把这段逻辑用synchronized包起来,然后ConcurrentHashMap和AtomicInteger都可以还原为简单的Map和Integer。但是这样的处理效率会低一些。

synchronized (this){
  // logic
}

另外,还可以使用ConcurrentHashMap的computeIfAbsent方法,它内部也使用了synchronized来保证线程安全,但是加锁的力度要比第一种方法细很多,效率也高一些。

return map.computeIfAbsent(name, v -> maxId.incrementAndGet());

详见 - https://enlear.academy/some-bugs-in-concurrenthashmap-you-should-know-eacc5e3cc209

标签:Map,ConcurrentHashMap,name,map,alice,安全,线程,id
From: https://www.cnblogs.com/maxstack/p/17790745.html

相关文章

  • 前端多线程处理 —— Promise对象
    在前端编程中,处理一些简短、快速的操作,在主线程中就可以完成。但是,在处理一些耗时比较长以至于比较明显的事情,比如读取一个大文件或者发出一个网络请求,就需要子线程来完成,以避免只用单线程时造成页面一时无法响应的事情。以发送网络请求为例,在以往的JavaScript中,使用多个回调函......
  • 智慧矿山保安全:人员越界识别AI算法实时预警
    煤矿作为一种危险性较高的工业领域,安全管理一直是煤矿企业的重要任务。传统煤矿安全管理主要依靠人工巡逻及视频监控等手段,但这些方法往往存在人力不足、盲区多等问题,无法实时监控和预警,难以有效避免事故的发生。随着人工智能技术的快速发展,智慧矿山AI算法系列应运而生,其中包括了人......
  • Java基础 多线程
    进程:进程是程序的基本执行实体(简单理解就是,一个软件运行之后,它就是一个进程)线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。简单说,线程就是应用软件中互相独立的、又可以同时运行的功能。如果这样的功能比较多,就形成了多线程 ......
  • 网络安全
    目录网络安全APAP4.0AP5.0数字签名密钥分发对称式密钥的分发非对称式密钥的分发安全电子邮件网络安全APAP4.0与AP5.0存在安全漏洞,它们必须建立在双方都确认是对方的密钥才安全,否则可能出现中间人攻击。下面AP4.0与AP5.0的介绍建立在能安全获得密钥的情况下,后面介绍如何......
  • 信息保密 追朔 信息安全 公司内外 水印 暗水印 盲水印
     公司内部系统一般考虑到信息泄密,都有系统水印和网页水印,文档水印。有些水印看不见,所以别拍照,乱截图随意传播。别给自己找麻烦。培训视频,技术文档,客户文档等都有可能使用了暗水印,不为肉眼所见。 阿里巴巴内网的不可见水印用的是什么算法?https://new.qq.com/rain/a/2022030......
  • 人工智能 | 无人驾驶汽车:道路安全和效率的未来
    随着科技的飞速发展,无人驾驶汽车正在逐渐改变我们的道路交通方式。这项革命性的技术不仅仅让我们能够实现更安全的驾驶,还将为交通系统带来更高效、更智能的未来。无人驾驶汽车如何成为道路安全和效率的未来代表。无人驾驶汽车的基础无人驾驶汽车的核心是人工智能(AI)和传感器技术。这......
  • 线程死循环中的sleep,误差10ms以上
    线程函数里是死循环,其中需要sleep1ms,即1ms周期执行死循环。但是经示波器测试发现至少10ms以上。【已测试的方法】Sleep(1);  //WindowsapiWaitForSingleObject(hThread,1);//Windowsapistd::this_thread::sleep_for(std::chrono::milliseconds(1)); //c++api【原......
  • Python threading实现多线程 提高篇 线程同步,以及各种锁
    本文主要讲多线程的线程之间的资源共享怎么保持同步。多线程基础篇见,Pythonthreading实现多线程基础篇Python的多线程,只有用于I/O密集型程序时效率才会有明显的提高,如文件/输入输出/socket网络通信/http通讯等待。对于计算密集型程序一般采用多进程,这里不多讲。一、多线程的......
  • Python threading实现多线程 基础篇
    讲多线程前,先要了解什么是进程,什么是线程,已经知道的请略过。一、进程与线程:进程是资源分配的最小单位,一个程序至少有一个进程。线程是程序执行的最小单位,一个进程至少有一个线程。进程都有自己独立的地址空间,内存,数据栈等,所以进程占用资源多。由于进程的资源独立,所以通讯不方......
  • 安全基础:数字信封、数字签名、数字证书
    总览 "数字证书"的实例:HTTPS协议下面,我们看一个应用"数字证书"的实例:https协议。这个协议主要用于网页加密。1.首先,客户端向服务器发出加密请求。2.服务器用自己的私钥加密网页以后,连同本身的数字证书,一起发送给客户端。3.客户端(浏览器)的"证书管理器",有"受信任的根......