并发编程之CASAtomic原子操作

目录

什么是CAS?

CAS 原子操作的三大问题

ABA问题

循环时间长开销大

只能保证一个共享变量的原子操作

jdk中的原子操作类

AtomicInteger

AtomicLong

AtomicReference

LongAdder


什么是CAS?

        CAS是“比较并交换”(Compare and Swap)的缩写。CAS是一种多线程同步的技术,用于实现多线程环境下的原子操作,常用于实现无锁的数据结构和线程安全算法。

        CAS 操作过程都包含三个运算符:一个内存地址 V,一个期望的值 A 和一 个新值 B,操作的时候如果这个地址上存放的值等于这个期望的值 A,则将地址 上的值赋为新值 B,否则不做任何操作。

        CAS 的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新 值,否则不做任何事儿,但是要返回原值是多少。自然 CAS 操作执行完成时,在 业务上不一定完成了,这个时候我们就会对 CAS 操作进行反复重试,于是就有了 循环 CAS。很明显,循环 CAS 就是在一个循环里不断的做 cas 操作,直到成功为 止。Java 中的 Atomic 系列的原子操作类的实现则是利用了循环 CAS 来实现。

CAS 原子操作的三大问题

ABA问题

        ABA问题是指在多线程环境下,一个值由A变为B,再由B变回A,这时如果仅使用CAS来判断值是否变化,可能无法正确地捕捉到中间的变化,从而导致不正确的结果。ABA问题包含以下步骤:

1. 初始时,某共享变量的值为A。

2. 线程1读取该共享变量的值为A,然后线程1被阻塞或者挂起。

3. 线程2将该共享变量的值由A改为B,再改回A。

4. 线程1恢复执行,使用CAS检查共享变量的值是否仍然为A,由于共享变量的值此时确实为A,CAS操作成功。

在以上过程中,线程1没有察觉到共享变量B发生过变化,因为CAS只检查共享变量的当前值和预期值是否相等。

解决ABA问题,可以使用带有版本号的变量,也被称为版本号引用。比如使用Atomic包下工具类AtomicStampedReference:

import java.util.concurrent.atomic.AtomicStampedReference;public class ABATest {public static void main(String[] args) {// 初始值为A,初始版本号为0AtomicStampedReference<String> atomicRef = new AtomicStampedReference<>("A", 0);// 线程1读取当前值和版本号int[] stampHolder = new int[1];String value1 = atomicRef.get(stampHolder);// 线程2修改值为B,并增加版本号atomicRef.compareAndSet(value1, "B", stampHolder[0], stampHolder[0] + 1);// 线程1再次尝试CAS,但由于版本号已经变化,不再匹配,不会发生CAS成功boolean success = atomicRef.compareAndSet("A", "C", stampHolder[0], stampHolder[0] + 1);System.out.println("CAS success: " + success);  // 输出false,表示CAS操作失败}
}
循环时间长开销大

       自旋 CAS 如果长时间不成功,会给 CPU 带来非常大的执行开销。

只能保证一个共享变量的原子操作

        当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操 作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来完成操作。


jdk中的原子操作类

AtomicInteger

get():获取当前值。

set(int newValue):设置为给定值。

getAndIncrement():获取当前值并递增。

compareAndSet(int expect, int update):比较当前值是否等于预期值,若相等则更新为新值。

import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerExample {private static AtomicInteger counter = new AtomicInteger(0);public static void main(String[] args) {System.out.println("Initial value: " + counter.get());int newValue = 10;counter.set(newValue);System.out.println("Set new value: " + counter.get());int oldValue = counter.getAndIncrement();System.out.println("Old value: " + oldValue + ", After increment: " + counter.get());int expectedValue = 10;int updateValue = 20;boolean updated = counter.compareAndSet(expectedValue, updateValue);System.out.println("Value updated? " + updated + ", Current value: " + counter.get());}
}
AtomicLong

get():获取当前值。

incrementAndGet():递增并获取新值。

getAndSet(long newValue):设置为给定值,并返回旧值。

import java.util.concurrent.atomic.AtomicLong;public class AtomicLongExample {private static AtomicLong counter = new AtomicLong(0);public static void main(String[] args) {System.out.println("Initial value: " + counter.get());long newValue = 10;counter.set(newValue);System.out.println("Set new value: " + counter.get());long incrementedValue = counter.incrementAndGet();System.out.println("Incremented value: " + incrementedValue);long oldValue = counter.getAndSet(20);System.out.println("Old value: " + oldValue + ", Current value: " + counter.get());}
}
AtomicReference

get():获取当前引用对象。

set(V newValue):设置为给定值。

compareAndSet(V expect, V update):比较当前引用对象是否等于预期值,若相等则更新为新值。

import java.util.concurrent.atomic.AtomicReference;public class AtomicReferenceExample {public static void main(String[] args) {// 初始值为 "Hello"AtomicReference<String> atomicReference = new AtomicReference<>("Hello");// 获取当前值String currentValue = atomicReference.get();System.out.println("Current Value: " + currentValue);// 尝试更新值,如果当前值是 "Hello",则更新为 "World"boolean updated = atomicReference.compareAndSet("Hello", "World");System.out.println("Updated: " + updated);// 获取更新后的值currentValue = atomicReference.get();System.out.println("Current Value: " + currentValue);// 尝试更新值,如果当前值是 "Hello",则更新为 "World123"updated = atomicReference.compareAndSet("Hello", "World123");System.out.println("Updated: " + updated);// 获取更新后的值currentValue = atomicReference.get();System.out.println("Current Value: " + currentValue);}
}

更多原子类可以去jdk的java.util.concurrent.atomic包下去查看。


LongAdder

       LongAdder 是 Java 中 java.util.concurrent.atomic 包下的原子操作类,用于高并发环境下对long类型的加法操作。它是在JDK8引入的,相比于 AtomicLong,LongAdder 在高并发情况下表现更好,因为它将内部的值分散到多个变量中,从而降低了竞争。LongAdder引入的初衷解决高并发环境下 AtomicLong 的自旋瓶颈问题。

相关API如下:

add(long x):将当前值增加 x

increment():将当前值增加1。

decrement():将当前值减少1。

sum():获取当前的总和值。注意,这个操作不是原子性的,因此在高并发情况下,它可能不是最准确的。

reset():将当前值重置为0。

import java.util.concurrent.atomic.LongAdder;public class LongAdderExample {public static void main(String[] args) throws InterruptedException {LongAdder longAdder = new LongAdder();// 创建多个线程并发增加值Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {longAdder.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {longAdder.increment();}});thread1.start();thread2.start();// 等待两个线程执行完毕thread1.join();thread2.join();// 获取最终的总和值long sum = longAdder.sum();System.out.println("Final Sum: " + sum);}
}

         除了LongAdder外,还有引入了它的三个兄弟类:LongAccumulator、 DoubleAdder、DoubleAccumulator,感兴趣的可以去java.util.concurrent.atomic去深入了解。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sentinel入门,转载的,不记得在哪复制的了

sentinel 基本概念 开发的原因&#xff0c;需要对吞吐量&#xff08;TPS&#xff09;、QPS、并发数、响应时间&#xff08;RT&#xff09;几个概念做下了解&#xff0c;查自百度百科&#xff0c;记录如下&#xff1a; 响应时间(RT)   响应时间是指系统对请求作出响应的时间。…

python按列写入数据到excel

要将数据按列写入 Excel&#xff0c;可以使用 Python 的 openpyxl 库。 首先&#xff0c;需要安装 openpyxl 库。可以使用以下命令在终端或命令提示符中安装&#xff1a; pip install openpyxl然后&#xff0c;可以按照以下步骤编写代码&#xff1a; 1.导入 openpyxl 库&…

Prokka: ubuntu安装的时候出现错误

[14:10:57] Running: cat /app/prokka_result/ref_file/ref_file.HAMAP.hmm.tmp.77.faa | parallel --gnu --plain -j 2 --block 108208 --recstart ‘>’ --pipe hmmscan --noali --notextw --acc -E 1e-09 --cpu 1 /opt/prokka/db/hmm/HAMAP.hmm /dev/stdin > /app/pro…

Linux 网络工具

sar 利用 sar 工具来监控网络情况&#xff0c;命令行形式&#xff1a; sar -n [keyword] [ <interval> [ <count> ] ]参数说明&#xff1a; -n&#xff1a;表示网络性能监控。 keyword 的取值如下&#xff1a; DEV&#xff1a;显示网络接口信息。EDEV&#xff…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpResponse的定义和初始化 以及组织 HttpResponse 响应消息

一、HttpResponse的定义 1.定义状态码枚举 // 定义状态码枚举 enum HttpStatusCode {Unknown 0,OK 200,MovedPermanently 301,MovedTemporarily 302,BadRequest 400,NotFound 404 }; 2.HTTP 响应报文格式 这个数据块主要是分为四部分 第一部分是状态行第二部分是响应…

Hyperledger Fabric 管理链码 peer lifecycle chaincode 指令使用

链上代码&#xff08;Chaincode&#xff09;简称链码&#xff0c;包括系统链码和用户链码。系统链码&#xff08;System Chaincode&#xff09;指的是 Fabric Peer 中负责系统配置、查询、背书、验证等平台功能的代码逻辑&#xff0c;运行在 Peer 进程内&#xff0c;将在第 14 …

【嵌入式-网络编程】vmware中使用UDP广播失败问题

问题描述&#xff1a; 自己在vmware中搭建了2台虚拟机&#xff0c;虚拟机A向虚拟机A和虚拟机B发送广播信息&#xff0c;接收端在虚拟机A和虚拟机B&#xff0c;这个时候&#xff0c;由于没配置sin.sin_addr.s_addr htonl(INADDR_ANY);&#xff0c;而是配置的inet_pton(AF_INET,…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpRequest模块 解析http请求协议

一、HTTP响应报文格式 HTTP/1.1 200 OK Bdpagetype: 1 Bdqid: 0xf3c9743300024ee4 Cache-Control: private Connection: keep-alive Content-Encoding: gzip Content-Type: text/html;charsetutf-8 Date: Fri, 26 Feb 2021 08:44:35 GMT Expires: Fri, 26 Feb 2021 08:44:35 GM…

今日实践 — 附加数据库/重定向失败如何解决?

WMS数据库与重定向 前言正文如何建立数据库连接&#xff1f;第一步&#xff1a;打开SSMS&#xff0c;右击数据库&#xff0c;点击附加第二步&#xff1a;点击添加第三步&#xff1a;找到自己的数据库文件&#xff0c;点击确定按钮第四步&#xff1a;若有多个数据库&#xff0c;…

如何使用静态IP代理解决Facebook多账号注册并进行网络推广业务?

在当今的数字时代&#xff0c;社交媒体成为了企业进行网络推广的一个重要途径&#xff0c;其中&#xff0c;Facebook是最受欢迎的社交媒体之一&#xff0c;因为它可以让企业通过创建广告和页面来推广他们的产品或服务。 但是&#xff0c;使用Facebook进行网络推广时&#xff0…

Spring Cloud GateWay实现熔断降级

熔断降级 当分布式系统中的网关接收到大量请求并向后端远程系统或服务发起调用时&#xff0c;后端服务可能会产生调用失败&#xff08;如超时或异常&#xff09;。这时&#xff0c;如果让请求继续堆积在网关上&#xff0c;可能会导致整个系统的瘫痪。因此&#xff0c;需要快速…

React16源码: React中Fiber对象的源码实现

关于 Fiber 对象 在FiberRoot里面&#xff0c;它也会为我们去创建的一个对象叫做 Fiber在 React16 之后, 非常核心&#xff0c;非常重要的一个东西A. 每个 ReactElement 都会对应一个 Fiber 对象B. 它会记录节点的各种状态 比如&#xff0c;class component&#xff0c;它的st…

面试算法101:分割等和子集

题目 给定一个非空的正整数数组&#xff0c;请判断能否将这些数字分成和相等的两部分。例如&#xff0c;如果输入数组为[3&#xff0c;4&#xff0c;1]&#xff0c;将这些数字分成[3&#xff0c;1]和[4]两部分&#xff0c;它们的和相等&#xff0c;因此输出true&#xff1b;如…

Docker 中使用超级用户

在docker中安装keytool产生的问题&#xff1a; sudo apt-get install openjdk-8-jre-headless bash: sudo: command not found elasticsearchd989639e3cb4:~/config/certs$ apt-get install openjdk-8-jre-headless E: Could not open lock file /var/lib/dpkg/lock-frontend …

【代码复现系列】paper:CycleGAN and pix2pix in PyTorch

或许有冗余步骤、之后再优化。 1.桌面右键-git bash-输入命令如下【git clone https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix】 2.打开anaconda的prompt&#xff0c;cd到pytorch-CycleGAN-and-pix2pix路径 3.在prompt里输入【conda env create -f environment.y…

【数值分析】逼近,正交多项式

逼近 由离散点&#xff08;函数表&#xff09;给出函数关系通常有两种方法&#xff1a; 使用多项式插值 使用多项式插值会带来两个问题&#xff1a;1. 龙格现象2. 数值本身带有误差&#xff0c;使用插值条件来确定函数关系不合理三次样条插值 三次样条插值克服了龙格现象&…

多线程在编程中的重要性有什么?并以LabVIEW为例进行说明

多线程在编程中的重要性体现在以下几个方面&#xff1a; 并行处理&#xff1a; 多线程允许程序同时执行多个任务&#xff0c;这在现代多核心处理器上尤其重要。通过并行处理&#xff0c;可以显著提高程序的执行效率和响应速度。 资源利用最大化&#xff1a; 通过多线程&#x…

工厂设计模式的思考

工厂模式对于开发者来说并不陌生&#xff0c;他利用多肽性很好的进行业务之间的解耦&#xff0c;不同的场景创建不同的实现&#xff0c;从而使得更多的关注业务实现,这种简单的实现这里不在举例。但是如果情形比较多的时候就会遇到问题&#xff0c;我们的工厂类就会产生大量的i…

Nacos与Eureka的区别详解

Nacos与Eureka的区别详解 在微服务架构中,服务注册与发现是核心组件之一,它们允许服务实例在启动时自动注册,并且能被其他服务发现,从而实现服务之间的互相通信。Nacos和Eureka都是现代微服务体系中广泛使用的服务注册与发现工具。本文将深入分析二者的区别,并为您提供一…

基于YOLOv7开发构建道路交通场景下CCTSDB2021交通标识检测识别系统

交通标志检测是交通标志识别系统中的一项重要任务。与其他国家的交通标志相比&#xff0c;中国的交通标志有其独特的特点。卷积神经网络&#xff08;CNN&#xff09;在计算机视觉任务中取得了突破性进展&#xff0c;在交通标志分类方面取得了巨大的成功。CCTSDB 数据集是由长沙…