最佳实践:AtomicInteger实现边界值控制

2023-05-16

最佳实践:AtomicInteger实现边界值控制

前言

这篇文章主要讲两部分,一部分简单的讲了一下AtomicInteger和LongAdder的实现对比,这部分不会讲太细,因为有更好的文章已经讲过了,而且像一些实现细节可能自己看代码会更好一些。另外一部分就讲讲AtomicInteger实现边界值控制,也就是本篇的标题,其实说这个的时候我估计很多人不知道具体讲啥,等我细细讲来的时候估计就会明白。

LongAdder

在JDK8之前需要做本机的计数器操作,一般都会做AtomicInteger(注:接下来统一用AtomicInteger来讲,就不会讲AtomicLong,本质上是一样)来进行,因为AtomicInteger是在硬件层面做CAS操作,所以性能也是非常之高的。当然在JDK8中给我们提供了另外一种可能那就是LongAdder。开始对这个类还是满心期望。而且AtomicInteger的性能已经够好,难道LongAdder有比较它更好吗?在追求极限性能的过程中没有终点。废话不多说鸟,进入正题。

LongAdder和AtomicInteger实现对比

在说LongAdder的实现之前不得不先说说AtomicInteger性能瓶颈在哪里,直接看代码:

    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

上面的代码是比较常用的incr操作,compareAndSet的实现是unsafe中的native实现CAS原子操作。这种原子操作的锁粒度非常小,能最大力度的减少锁等待的开销。但是这里也有一个很大的问题。可以看代码的最外层是一个for循环,当并发请求非常高的时候,失败率会非常高,CPU就会一直自旋等待,导致的结果就是性能下降。所以LongAdder要做的就是减少碰撞带来的性能损耗,其实现的思想和ConCurrentHashMap极其的相似,进行分桶,每一个小桶相互独立,这样就可以减少CAS的失败率从而提高性能,具体可以参见这篇文章:从LongAdder看更高效的无锁实现。我想大多数人看到这种实现既有种豁然开朗又有种似曾相识的感觉。对的,目前来看所有解决性能瓶颈的方向基本上都是朝着水平扩展的方向去,单点不行就多点。

LongAdder是否真的可以替代AtomicInteger

我一直信奉一句话:“古人有云,鱼和熊掌不可兼得。得到一些东西的同时总需要付出一些代价”。所以LongAdder在提高性能的同时也牺牲了一些东西。

    public void add(long x) {
        Cell[] as; long b, v; HashCode hc; Cell a; int n;
        if ((as = cells) != null || !casBase(b = base, b + x)) {
            boolean uncontended = true;
            int h = (hc = threadHashCode.get()).code;
            if (as == null || (n = as.length) < 1 ||
                (a = as[(n - 1) & h]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))
                retryUpdate(x, hc, uncontended);
        }
    }
    public void increment() {
        add(1L);
    }    

刚才在上面没有讲代码,在这里一定要把代码贴出来,具体看increment操作,这里可以对比AtomicInteger的incrementAndGet操作,其区别是AtomicInteger有返回值,而LongAdder是没有返回值。可能大部分场景下并不需要关心实时的返回值。但是有些场景下就需要关心。比如说某些场景下需要关心边界值。比如说本地的库存扣减,需要严格校验库存为0这个值,LongAdder做不到这一点(其代码注释里面也讲了获取值并非原子性),从这里可以看出LongAdder并非在所有的场景下都能取代AtomicInteger。

AtomicInteger实现边界值控制

AtomicInteger并非自己实现了边界值控制,其实在某些场景下我们可能需要这样一种实现,直接上代码:

    public final Result<Integer> incrementAndGet(int upperBound) {
        for (;;) {
            int current = get();
            if(current>=upperBound){
                //如果当前值已经到了最大值,则直接返回
                return new Result<Integer>(COUNT_BOUNDS,current);
            }
            int next = current + 1;
            if (compareAndSet(current, next)){
                return new Result<Integer>(next);
            }
        }
    }
    public final Result<Integer> decrementAndGet(int lowerBound) {
        for (;;) {
            int current = get();
            if(current<=lowerBound){
                //如果当前值已经小于最小值,则直接返回
                return new Result<Integer>(COUNT_BOUNDS,current);
            }
            int next = current - 1;
            if (compareAndSet(current, next))
                return new Result<Integer>(new Integer(next));
        }
    }    

这样通过对AtomicInteger的一些简单的扩展就可以实现边界值控制,而且实现完全是原子操作。

LongAdder是否可以实现边界值的控制

这个问题我简单的想了一下,如果让LongAdder去实现这个功能,那么它就不是LongAdder了。这里先看一段代码:

    public long sum() {
        long sum = base;
        Cell[] as = cells;
        if (as != null) {
            int n = as.length;
            for (int i = 0; i < n; ++i) {
                Cell a = as[i];
                if (a != null)
                    sum += a.value;
            }
        }
        return sum;
    }

这段代码是获取LongAdder的值,注意这里并没有任何的锁操作,其实现也是把所有的桶的值都加起来。所以如果要实现边界值控制,就意味着每一次incr或者decr操作之前都要sum()一下,而且是需要加锁的,其性能显然不会高,会比AtomicInteger还要慢很多。这种尴尬的问题其实在单库分库分表之后也会碰到,当要count(*)的时候会发现实现不了或者成本比较大。所以也就如同我前面所说过的得到一些东西的同时总需要付出一些代价。LongAdder在追求极限性能的同时也牺牲了其他方面的一些可扩展性。

总结

  1. 学习一下LongAdder解决高并发下的性能问题,其主要思想是通过分桶来解决锁等待的问题。从LongAdder的源码也可以看出显然没有AtomicInteger那么丰富,为了性能也舍弃了很多的功能。所以目前来看LongAdder不会完全替代AtomicInteger,主要还取决于一些具体使用场景。
  2. 对AtomicInteger进行扩展实现边界值的控制,实现一些特殊需求,比如上面说的边界值控制。显然通过LongAdder是无法实现这种扩容。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

最佳实践:AtomicInteger实现边界值控制 的相关文章

  • 黑苹果10.15.7使用

    咸鱼 由于懒得去折腾硬件 xff0c 所以直接在闲鱼上买了一个现成的 xff0c 已经安装了10 15 7 xff0c i3 8100 xff0c 256G的ssd xff0c 16G内存 两天从汕头到杭州 xff0c 不得不感叹当今快递业
  • 访问控制的几种模式

    一般分类 自主访问控制 xff1a Discretionary Access Control xff0c DAC 由客体的属主对自己的客体进行管理 xff0c 由属主自己决定是否将自己的客体访问权或部分访问权授予其他主体 xff0c 这种控
  • linux进程内存查看方法

    通过proc procid status查看进程内存 piky 64 piky WRT WX9 src test cat proc 3118 status Name a out Umask 0002 State S sleeping Tgi
  • 网络设备单点登录

    1 深信服 深信服的单点登录非常丰富 1 1 AD单点登录 1 1 1 通过域下发登录脚本的单点登录 通过AD服务器的组策略推送客户端的登录核退出程序 xff0c 两个exe程序 xff0c 实现相对容易 xff0c 但非windows客户
  • 基于openldap查询AD用户的组信息,上代码

    当用户找不到时 xff0c 这里的ldap result会卡住 xff0c 设置tm sec 为1也不行 需要继续定位 同样系统上官方的ldapsearch程序没有这个问题 span class token comment gcc ldap
  • linux 下记录ssh的操作日志

    ssh name 64 x x x x tee a log log tee可以读取标准输入输出的数据 xff0c 将其写到文件中
  • 云安全做什么?

    现在还说不清楚 xff0c 留下标题 xff0c 等能说清楚了后再补充
  • 用jpcap读取pcap抓包文件并做简单的过滤显示

    已经完成了最简单的从pcap文件中读取每个报文 xff0c 然后再转换成文本格式显示报文头信息 xff0c 以便于后面分析用 xff0c 这里做了简单的过滤 xff0c 根据源目的ip地址 xff0c 或者查找tcp没有响应的报文 以后又空
  • 【吐血整理】Java项目源码分享

    java servlet 43 jsp 43 bean开发开源宅商城系统 xff0c 未用任何java开源框架 实例 xff08 七 xff09 xff0c 电商书店 xff0c 源码下载 xff1a jsp 43 servlet仿当当网电
  • django操作数据库使用中文的方法

    今天在学习django时 xff0c 向mysql中写入数据 xff0c 结果提示错误 django db utils DatabaseError 1366 34 Incorrect string value 39 在网上找了很多资料 xf
  • simple_html_dom使用小结

    简单范例 lt php include 34 simple html dom php 34 Create DOM from URL or file html 61 file get html 39 http www google com 3
  • 启动Tomcat服务报错,端口占用解决方案

    启动Tomcat服务器报错 xff1a Several ports 8005 8080 8009 required by Tomcat v5 5 Server at localhost are already in use The serv
  • 人脸识别经典算法一:特征脸方法(Eigenface)

    这篇文章是撸主要介绍人脸识别经典方法的第一篇 xff0c 后续会有其他方法更新 特征脸方法基本是将人脸识别推向真正可用的第一种方法 xff0c 了解一下还是很有必要的 特征脸用到的理论基础PCA在另一篇博客里 xff1a 特征脸 Eigen
  • 装系统问题,BIOS显示不出硬盘解决方案

    原创作品 转载请注明出处 谢谢 杨福星 http blog csdn net luckystar92 article details 51265484 一 背景 华硕电脑 原装win8系统 改装win7系统出现BIOS显示不出硬盘问题 能进
  • 2014年计算机求职总结--准备篇

    版权所有 xff0c 转载请注明出处 xff0c 谢谢 xff01 http blog csdn net walkinginthewind article details 13000431 找工作是一个长期准备的过程 xff0c 突击是没什
  • Spring框架---全面详解【无比详细,学习总结】

    Spring框架 一 IoC 1 Spring的目标 集成 整合 优势 xff1a 解耦合 侵入小 轻量级 2 IoC控制反转 将控制权 xff08 创建对象 xff09 从调用方转义到Spring容器 以前对象的创建是由我们开发人员自己维
  • 关于kali gnome-tweaks-tool无法找到命令问题

    输入gnome tweaks即可
  • 在 Ubuntu 中添加新用户并给予 root 权限

    在 Ubuntu 中添加新用户并给予 root 权限的步骤如下 xff1a 打开终端 xff0c 以 root 身份登录 sudo su 使用 adduser 命令添加新用户 adduser username 为新用户设置密码 passwd
  • tensorflow载入数据的三种方式

    Tensorflow数据读取有三种方式 xff1a Preloaded data 预加载数据Feeding Python产生数据 xff0c 再把数据喂给后端 Reading from file 从文件中直接读取 这三种有读取方式有什么区别
  • 网络数据包封装与解封装基本过程

    数据包 应用层 TCP头 xff1a 源端口 43 目的端口 数据包 传输层 IP头 xff1a 源 IP 地址 43 目的 IP 地址 TCP头 xff1a 源端口 43 目的端口 数据包 网络层 Ethernet头 xff1a 源 MA

随机推荐

  • 环形缓冲区

    什么是环形缓冲区 在通信程序中 xff0c 经常使用环形缓冲区作为数据结构来存放通信中发送和接收的数据 环形缓冲区是一个先进先出 的循环缓冲区 xff0c 可以向通信程序提供对缓冲区的互斥访问 环形缓冲区原理 环形缓冲区通常有一个读指针 和
  • win10蓝牙链接上的标准串行com口无法删除

    Bluetooth 链接上的标准串行设备 无法删除解决办法 一 原理 Windows系统每次使用蓝牙与手机 xff08 耳机等蓝牙设备 xff09 配对时 xff0c 就会自动分配一个串口 xff0c 如果该蓝牙设备一直处于保留状态 xff
  • 创新工场两道笔试题0919

    题目1 字符串去重 xff0c 老题目 xff0c 只是要求不能开辟新空间用来复制原字符串 思路 xff1a 使用布尔型的简单hash表可以节省空间 xff0c 用来存储字符是否出现的信息 xff0c 刚开始hash表里面都是false x
  • Dockerfile构建镜像读取环境变量问题

    起初使用如下Dockerfile构建Java镜像 xff1a FROM span class token number 10 170 span 33 245 base openjdk 11 0 6 jdk slim buster MAINT
  • Manjaro-i3的安装

    Manjaro i3的安装 manjaro的安装方法记录在这里 xff0c 其中涉及到一些常用软件的安装 xff0c 此处只介绍Manjaro i3的安装 xff0c 并对一些安装配置中出现的问题列出解决方法 写在前面 之前用Win10的时
  • 解决Error:Configuration with name 'debug' not found.

    在同步开发时 同事添加了某个module 自己同步代码后会出现Error Configuration with name 39 default 39 not found 的错误 这是因为同步时不会自动把远程仓库下载下来需要你自己初始化下载
  • Altium Designer布局布线基本规则设置

    PCB布局布线过程中 xff0c 有如下几项最基本的规则需要设置 1 间距规则 一般情况下设置三个不同的间距规则即可 即整板间距 ALL ALL xff0c 一般密度的板子 6mil间距即可 如果有 BGA 封装 xff0c 或者需要控制阻
  • 使用环形缓冲区ringbuffer实现串口数据接收

    文章目录 1 ringbuffer简单介绍2 ringbuffer的代码实现2 1 ringbuffer数据结构定义2 2 ringbuffer初始化2 3 ringbuffer写数据2 4 ringbuffer读数据 3 在串口中使用ri
  • 在线刷机详细教程(图文)〓诺基亚——NSS修改CODE+NSU升级

    导读 xff1a 在线刷机详细教程 图文 诺基亚 NSS修改CODE 43 NSU升级 上次帮零刷机的时候发生了好多问题 费了好久的时间才搞定 主要是之前都没做好细致的工作 对刷机没有太深的了解 所以我用了一点时间把刷机教程再重新的整理一下
  • angularJS绑定数据时自动转义html字符串

    angular js转换字符串形式的html标签 在保存数据到数据库的时候有些保存一个商品的描述数据 会直接保存这个文件描述中的一些html标签 当在数据库拿这个数据的时候用angular显示会直接字符串 显示下列这样 span class
  • 【K8S 一】使用kubeadm工具快速部署Kubernetes集群(单Master)

    此为安装部署单Master集群 xff0c 如需高可用Master集群 xff0c 请一并参考 K8S 五 使用kubeadm工具快速部署Kubernetes集群 Master高可用集群 目录 安装前配置 依赖包安装 kube proxy开
  • [Scala Shell脚本执行]

    Scala语言来自于Scalable xff08 可伸缩的 xff09 xff0c 既可以写一些小的脚本 xff0c 又可以写一写复杂的服务器端的程序 scala支持源文件解释执行 xff0c jar执行 xff0c 各有利弊 本文主要介绍
  • 全息投影技术

    1 概念 全息投影技术 xff08 front projectedholographic display xff09 也称 虚拟成像 技术是利用干涉和衍射原理记录并再现物体真实的 三维 图像的技术 全息投影技术不仅可以产生立体的空中幻像 x
  • Android Settings源码结构分析与自实现

    最近的项目一直是按照PRD与高清 xff0c 修改系统设置 xff0c 调整布局 间距 颜色 xff0c 涉及到一些流程的更改与自定义控件 xff0c 以及对settings源码结构的研究 在项目相对空闲是 xff0c 做个整理记录 由于项
  • 程序员的职业规划

    在这个节点讲讲我对职业发展路径的理解 xff0c 另外就是理一理脑子里的一些思路 过段时间再看此文又可能是另一番理解 很多路从后往前看的时候总会有遗憾 xff0c 要是当初怎么怎么样就会怎么怎么样 导致这样的原因的第一个原因是人在做决策的时
  • 深度思考Java成员变量的初始化

    写Java代码的时候很少去关注成员变量的声明和初始化顺序 xff0c 今天借此机会抛出一些问题 xff1a 语言的设计者们为什么会这样设计 xff1f 比如说很常见的一个问题 xff1a abstract xff08 抽象 xff09 类不
  • 生产者消费者模型(二)-引入ArrayBlockingQueue

    前言 在 生产者消费者模型你知道多少 中简单的模拟了一个生产者消费者模型 有些网友对我的实现提出了很多质疑 我在文章的结尾也对抛出了一个问题 xff1a 在添加的过程中可能出现数据丢失的情况 xff0c 应该怎么处理 xff1f 在代码中也
  • 存储系统的实现-探析存储的机制和原理

    这一篇主要想写写一些自己对于存储的思考和领悟 xff0c 因为有些东西自己实践过 xff0c 所以感触过更加深一些 xff0c 技术上我还是认为自己实现和看别人的代码在感触上是不同的 这里假设一个图书馆 xff0c 假如说书就是要我们要放的
  • 毕业四年

    直至目前毕业已经快四年 xff0c 我的经历代表了大多数普通程序员的经历 xff0c 没有牛人传奇的经历 xff0c 甚至连进入这个职业都是一种偶然 xff0c 只是因为分数不够被调剂到计算机专业 很多人可能会有和我一样的经历甚至一样的困惑
  • 最佳实践:AtomicInteger实现边界值控制

    最佳实践 xff1a AtomicInteger实现边界值控制 前言 这篇文章主要讲两部分 xff0c 一部分简单的讲了一下AtomicInteger和LongAdder的实现对比 xff0c 这部分不会讲太细 xff0c 因为有更好的文章