ConcurrentMap的相关特点和使用

概述

ConcurrentMap是Java中的一个接口,主要扩展了Map接口,用于在多线程环境中提供线程安全的Map实现,是Java.util.concurrent包中的一部分,提供了一些原子操作,这些操作不需要使用synchronized关键字,从而提高了并发访问的效率。

currentMap接口定义了一些原子操作,例如putIfabsent、remove和replace操作等等,这些操作确保了在多线程环境中对共享数据的一致性和线程安全性。

在这里插入图片描述

底层实现

ConcurrentMap的一个典型实现是ConcurrentHashMap,在Java 8之前,ConcurrenMap使用的是分段式技术来提高并发访问时的效率,每个段本质上是一个小的HashTable,他有自己的锁。只有访问同一个段的线程才会相互阻塞,不同段的线程可以同时进行。
从Java8开始,ConcurrentMap的实现被改进,使用了一种不同的锁机制,被称为Synchronized State-Dependent Operations(SDO) 。这种机制通过使用CAS(Compare And Swap)操作和synchronized关键字来减少锁的使用,从而进一步提高并发性能。

优缺点

优点

  • 线程安全: ConcurrentMap提供了线程安全的Map实现,可以在多线程环境中安全使用。
  • 高并发性能: 相比Hashtable和Collections.synchronizedMap,ConcurrentMap的实现(如ConcurrentHashMap)提供更好的并发性能。
  • 原子性操作:Concurrent的院子操作方法减少了需要手动同步的需求。

缺点

  • 内存开销: 为了实现线程安全,ConcurrentMap可能会比非线程安全的Map实现使用更多的内存。
  • 复杂性: ConcurrentMap的内部工作机制比我们常见的Map的更加复杂。

示例

展示ConcurrentMap的原子操作

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ConcurrentMapExample {
    public static void main(String[] args) {
        ConcurrentMap<String, String> map = new ConcurrentHashMap<>();

        // 原子操作:putIfAbsent
        map.putIfAbsent("key1", "value1");
        System.out.println(map.get("key1")); // 输出 value1

        // 原子操作:replace
        map.replace("key1", "value1", "newValue1");
        System.out.println(map.get("key1")); // 输出 newValue1

        // 原子操作:remove
        map.remove("key1", "newValue1");
        System.out.println(map.get("key1")); // 输出 null,因为key1已被移除
    }
}

ConcurrentMap主要解决的问题

ConcurrentMap主要用于解决多线程环境中的数据共享问题。在多线程应用程序中,多个线程可能会同时读写共享的Map。如果没有适当的同步,这可能会导致数据不一致和线程安全问题。

ConcurrentMap通过提供线程安全的Map实现,确保了数据的一致性,同时提供了比完全同步的Map更好的并发性能。它广泛用于需要高并发访问共享数据结构的场景,例如缓存、跟踪资源的状态和统计数据等。

使用ConcurrentMap实现一个秒杀系统

在模拟商城秒杀的场景中,我们需要确保商品的库存更新是线程安全的,并且能够处理高并发的请求。使用ConcurrentMap可以帮助我们实现这一目标。下面是一个简单的秒杀系统的示例,使用ConcurrentHashMap来存储商品的库存,并通过原子操作来更新库存。

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

public class FlashSaleService {
    private ConcurrentMap<String, AtomicInteger> productStockMap = new ConcurrentHashMap<>();

    public FlashSaleService() {
        // 初始化商品库存
        productStockMap.put("product1", new AtomicInteger(100));
    }

    /**
     * 尝试购买商品
     *
     * @param productId 商品ID
     * @param quantity  购买数量
     * @return 购买是否成功
     */
    public boolean tryPurchase(String productId, int quantity) {
        // 获取商品库存
        AtomicInteger stock = productStockMap.get(productId);
        if (stock == null) {
            throw new IllegalArgumentException("Product does not exist");
        }

        // 循环尝试更新库存
        while (true) {
            int currentStock = stock.get();
            // 库存不足
            if (quantity > currentStock) {
                return false;
            }
            // 使用CAS操作更新库存
            if (stock.compareAndSet(currentStock, currentStock - quantity)) {
                System.out.println("Purchased successfully! Remaining stock: " + stock.get());
                return true;
            }
            // 如果CAS操作失败,循环重试
        }
    }

    public static void main(String[] args) {
        FlashSaleService service = new FlashSaleService();

        // 模拟多线程环境下的秒杀
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                boolean result = service.tryPurchase("product1", 1);
                if (result) {
                    System.out.println(Thread.currentThread().getName() + " got a deal!");
                } else {
                    System.out.println(Thread.currentThread().getName() + " failed to purchase.");
                }
            }).start();
        }
    }
}

在这个示例中,我们创建了一个FlashSaleService类,它有一个ConcurrentMap来存储商品的库存。我们使用AtomicInteger来表示库存数量,因为AtomicInteger提供了原子操作来保证在多线程环境下的线程安全。

tryPurchase方法是核心方法,它尝试为用户购买商品。它首先检查库存是否充足,然后使用compareAndSet方法来原子地更新库存数量。如果在尝试更新库存时库存已经被其他线程修改,compareAndSet会返回false,此时我们会重试直到成功或者库存不足。

在main方法中,我们模拟了1000个线程同时进行秒杀。每个线程尝试购买1个单位的商品。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/465654.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SAP前台处理:销售业务集成<VA03/VL03N/VLPOD/VF03) 01/02

一、背景&#xff1a; 从销售订单创建VA01>发货过账VL01N >POD确认>VF01开票 这个流程涉及的凭证流及各个节点如何查询上游下游凭证&#xff1b; 二、凭证流&#xff1a; 从销售订单查看销售凭证流 VA03 双击交货单&#xff1a;带出交货单对应行项目及分批次项目…

一周学会Django5 Python Web开发-Jinja3模版引擎-模板语法

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计37条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

Jest:JavaScript的单元测试利器

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Spring炼气之路(炼气二层)

一、bean的配置 1.1 bean的基础配置 id&#xff1a; bean的id&#xff0c;使用容器可以通过id值获取对应的bean&#xff0c;在一个容器中id值唯一 class&#xff1a; bean的类型&#xff0c;即配置的bean的全路径类名 <bean id"bookDao" class "com.zhang…

Docker 安装 Skywalking以及UI界面

关于Skywalking 在现代分布式系统架构中&#xff0c;应用性能监控&#xff08;Application Performance Monitoring, APM&#xff09;扮演着至关重要的角色。本文将聚焦于一款备受瞩目的开源APM工具——Apache Skywalking&#xff0c;通过对其功能特性和工作原理的详细介绍&am…

ISTJ型人格的心理问题

什么是ISTJ型人格 ISTJ型人格&#xff0c;来源于mbti职业性格测试&#xff0c;代表的是内向&#xff0c;实感&#xff0c;理智&#xff0c;独立&#xff0c;ISTJ型人格则是一种以认真&#xff0c;安静&#xff0c;负责任为显著特征的人格&#xff0c;具有这种人格的人&#xf…

leetcode 3080

leetcode 3080 题目 例子 思路 创建数组&#xff0c;记录nums 的值 对应的id, 按照大小排序。 代码实现 class Solution { public:vector<long long> unmarkedSumArray(vector<int>& nums, vector<vector<int>>& queries) {vector<long…

【回溯专题】【蓝桥杯备考训练】:n-皇后问题、木棒、飞机降落【未完待续】

目录 1、n-皇后问题&#xff08;回溯模板&#xff09; 2、木棒&#xff08;《算法竞赛进阶指南》、UVA307&#xff09; 3、飞机降落&#xff08;第十四届蓝桥杯省赛C B组&#xff09; 1、n-皇后问题&#xff08;回溯模板&#xff09; n皇后问题是指将 n 个皇后放在 nn 的国…

C++学习基础版(一)

目录 一、C入门 1、C和C的区别 2、解读C程序 3、命名空间 4、输入输出 &#xff08;1&#xff09;cout输出流 &#xff08;2&#xff09;endl操纵符 &#xff08;3&#xff09;cin输入流 二、C表达式和控制语句 1、数据机构 特别&#xff1a;布尔类型bool 2、算数运…

基于springboot的医院后台管理系统

采用技术 基于springboot的医院后台管理系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 患者管理 公告信息管理 公告类型管理 项目背景 互联网概…

【对顶队列】【中位数贪心】【前缀和】100227. 拾起 K 个 1 需要的最少行动次数

本文涉及知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 对顶队列&#xff08;栈&#xff09; 分类讨论 LeetCode100227. 拾起 K 个 1 需要的最少行动次数 给你一个下标从 0 开始的二进制数组 nums&#xff0c;其长度为 n &#x…

软件功能测试内容有哪些?湖南长沙软件测评公司分享

软件功能测试主要是验证软件应用程序的功能&#xff0c;且不管功能是否根据需求规范运行。是通过给出适当的输入值&#xff0c;确定输出并使用预期输出验证实际输出来测试每个功能。也可以看作“黑盒测试”&#xff0c;因为功能测试不用考虑程序内部结构和内部特性&#xff0c;…

#QT(事件--快捷键保存文件)

1.IDE&#xff1a;QTCreator 2.实验&#xff1a;QEvent,QMouseEvent,QKeyEvent。 在上一个文本编辑器的基础上实现快捷键"ctrls"保存文件。 3.记录 &#xff08;1&#xff09;查看QEVENT的有效事件 &#xff08;2&#xff09; 所有时间均继承于QEvent&#xff0c;任…

数学建模-邢台学院

文章目录 1、随机抽取的号码在总体的排序2、两端间隔对称模型 1、随机抽取的号码在总体的排序 10个号码从小到大重新排列 [ x 0 , x ] [x_0, x] [x0​,x] 区间内全部整数值 ~ 总体 x 1 , x 2 , … , x 10 总体的一个样本 x_1, x_2, … , x_{10} ~ 总体的一个样本 x1​,x2​,……

3_springboot_shiro_jwt_多端认证鉴权_Redis缓存管理器

1. 什么是Shiro缓存管理器 上一章节分析完了Realm是怎么运作的&#xff0c;自定义的Realm该如何写&#xff0c;需要注意什么。本章来关注Realm中的一个话题&#xff0c;缓存。再看看 AuthorizingRealm 类继承关系 其中抽象类 CachingRealm &#xff0c;表示这个Realm是带缓存…

使用Arthas

Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具。支持 JDK6&#xff0c; 采用命令行交互模式&#xff0c;可以方便的定位和诊断 线上程序运行问题。Arthas 官方文档十分详细&#xff0c;详见&#xff1a;https://alibaba.github.io/arthas 1、下载 代码托管地址之一…

不想当智能手表游戏掌机MP4的开发板不是好86盒

有道是&#xff0c;生活不易&#xff0c;多才多艺。 只是没想到有一天连开发板也能适用这句话。 你以为它只是一个平平无奇的智能家居86盒。 但它必要时它也可以化身智能手表。 或者是一个能随身携带的MP4。 甚至可以是一个能玩植物大战僵尸的触屏游戏掌机&#xff01; 项目简…

【CSP试题回顾】202212-3-JPEG 解码

CSP-202212-3-JPEG 解码 关键点&#xff1a;Z字扫描 在JPEG压缩中&#xff0c;Z字形扫描是一种将8x8块的数据按照Z字形&#xff08;或之字形&#xff09;顺序重新排列的过程。这样做的目的是为了将相似的数据&#xff08;尤其是零值&#xff09;放置在一起&#xff0c;从而提高…

windows 安装cuda 11.2过程记录

参考&#xff1a; https://blog.csdn.net/m0_45447650/article/details/123704930 https://zhuanlan.zhihu.com/p/99880204?from_voters_pagetrue 在显卡驱动被正确安装的前提下&#xff0c;在命令行里输入nvidia-smi.exe 下载CUDA Toolkit: https://developer.nvidia.com/…

C++ Qt开发:QTcpSocket网络通信组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QTcpSocket组件实现基于TCP的网络通信…