Java线程CAS原子操作

这次分享一些关于原子操作(CAS)的东西.

定义

CAS(Compare And Swap)是CPU的一个指令级别的操作,叫原子操作,原子操作是不可分割的,跟事务差不多,要么全部执行完成,要么不执行;

像这种操作有点类似阻塞锁机制,但是使用阻塞锁机制去控制的话,会比较消耗性能,而使用CAS操作的话会比使用锁更快。

synchronized性能比较,最简单的一个测试Demo

public class AtomicNumber {AtomicInteger ai = new AtomicInteger(0);int i = 0;public synchronized void addInteger() {i ++ ;}public void addAtomicInteger() {ai.getAndIncrement();}public static void main(String[] args) throws InterruptedException {long time = System.currentTimeMillis();final AtomicNumber atomicNumber = new AtomicNumber();final CountDownLatch latch = new CountDownLatch(2);int count = 0;while(count<2) {new Thread(new Runnable() {public void run() {for(int j=0;j<200000;j++)atomicNumber.addAtomicInteger();latch.countDown();}}).start();count++;}latch.await();System.out.println("花费的时间:"+(System.currentTimeMillis()-time) + "ms");System.out.println("atomicInteger value:"+atomicNumber.ai.get());final CountDownLatch latch1 = new CountDownLatch(2);count = 0;time = System.currentTimeMillis();while(count<2) {new Thread(new Runnable() {public void run() {for(int j=0;j<200000;j++)atomicNumber.addInteger();latch1.countDown();}}).start();count++;}latch1.await();System.out.println("花费的时间:"+(System.currentTimeMillis()-time) + "ms");System.out.println("i value:"+atomicNumber.i);}
}//运行的结果:
花费的时间:14ms
atomicInteger value:400000
花费的时间:35ms
i value:400000
//很明显,使用原子操作会比使用锁机制要快。

CAS里面有三个操作数:1、内存地址(V);2、期望的值(A);3、新值(B)

主要思想:如果内存地址V的期望值等于A时,则将地址V赋值给新值B,如果不相等,算CAS操作失败则不做任何操作;但触发了CAS操作,如果内存地址上的值V不等于A的话,就会进入死循环,一直做CAS操作,一直到相等也就是成功,这个循环过程叫自旋

存在的问题

1、ABA问题:即当取出内存地址V的时候,期望值从A变成新值B,然后有从B变成A值,然后再将V与期望值相比发现一值,其实这个过程确实发生了变化,只是结果值与初始值一致;

像这样的问题,我们可以采用两个方式,第一种就是在期望值变化的时候加上一个版本号(AtomicStampedReference)从A1变成B2变成A3这样就能解决这样的问题了,另外一种就是变化了就标记期望值已变化(AtomicMarkableReference)。

2、开销比较大:长期处于自旋的CAS操作,会导致性能消耗。

3、只能保证只有一个共享变量进行原子操作,在Java中有个专门处理的类来解决这个问题(AtomicReference)

JDK提供的原子操作类

AtomicInteger

用法在上面的那个案例有了就不多说了,有几个需要注意的地方:

1、在使用自增的方法有两个:1)、getAndIncrement() 这个是先获取值,在自增。2)、incrementAndGet() 先自增然后在获取值。这两个方法的差别有点类似 i++ 以及 ++i的操作。

AtomicReference

这个是原子操作引用类型,结果来看,原子引用并不会改变原始的值。

    static AtomicReference<UserInfo> reference = new AtomicReference<UserInfo>();public static void main(String[] args) {UserInfo userInfo1 = new UserInfo("Mark",12);reference.set(userInfo1);UserInfo userInfo2 = new UserInfo("Mark12",23);reference.compareAndSet(userInfo1, userInfo2);System.out.println("new data:");System.out.println(reference.get().getName());System.out.println(reference.get().getAge());System.out.println("old reference object:");System.out.println(userInfo1.getName());System.out.println(userInfo1.getAge());}static class UserInfo {String name;Integer age;public UserInfo(String name, Integer age) {super();this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}}//运行结果:
new data:
Mark12
23
old reference object:
Mark
12

AtomicStampedReference

在期望值变化的时候加上一个版本号,在做CAS操作的时候,如果不一致会返回为false,并且不会做任何操作

	/*** <p>需要一个初始值以及初始版本号*/static AtomicStampedReference<String> reference = new AtomicStampedReference<String>("Java",0);public static void main(String[] args) throws InterruptedException {final String value = reference.getReference();final int stamp = reference.getStamp();System.out.println("value:" + value + "====== version:"+stamp);Thread correctThread = new Thread(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName() + " old reference:"+value + " version:"+stamp + " CAS STATUS : " + reference.compareAndSet(value, value + " is the beast Language", stamp, 1));System.out.println(Thread.currentThread().getName() + "value:" + reference.getReference() + "====== version:"+reference.getStamp());}});Thread errorThread = new Thread(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName() + " old reference:"+reference.getReference() + " version:"+reference.getStamp() + " CAS STATUS : " + reference.compareAndSet(value, value + " Hello World!!", stamp, 2));System.out.println(Thread.currentThread().getName() + "value:" + reference.getReference() + "====== version:"+reference.getStamp());}});correctThread.start();correctThread.join();errorThread.start();errorThread.join();System.out.println("value:" + reference.getReference() + "====== version:"+reference.getStamp());}//运行结果:
value:Java====== version:0
value:Java====== version:0
Thread-0 old reference:Java version:0 CAS STATUS : true
Thread-1 old reference:Java version:0 CAS STATUS : false
Thread-0value:Java is the beast Language====== version:1
Thread-1value:Java is the beast Language====== version:1

目前就这么多啦,至于AtomicMarkableReference这个原子引用大家了自己去试一下跟AtomicStampedReference差不多,好啦该洗洗睡了。

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

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

相关文章

python 导航栏_解析导航栏的url--selnium,beautifulsoup实战

前段时间做ui自动化测试的时候&#xff0c;导航栏菜单始终有点问题&#xff0c;最后只好直接获取到url&#xff0c;然后直接使用driver.get(url)进入页面&#xff1b;包括做压测的时候&#xff0c;比如我要找出所有报表菜单的url&#xff0c;这样不可能手动去一个一个找出来&am…

PNG图片详解

1、PNG图片类型 PNG格式有8位、24位、32位三种&#xff0c;下面是一些术语&#xff1a; 索引透明&#xff1a;类似于GIF&#xff0c;某一像素只有全透和全不透明两种效果Alpha透明&#xff1a;半透明PNG8 8位的PNG最多支持256&#xff08;2的8次方&#xff09;种颜色&#xff0…

Java并发编程之显式锁(Lock)使用

又是一个基于AQS好用的类&#xff0c;看来下次有必要看看AQS了&#xff0c;正好又是放假。 既然叫显式锁&#xff0c;必然也有隐式锁&#xff0c;也就是所谓的synchronzied关键字&#xff0c;它们两者的区别呢在于使用范围&#xff0c;synchronzied关键字的使用范围比Lock要小…

python pychart三维_详解python模块pychartdir安装及导入问题

在迁移别人写好的脚本时&#xff0c;发现pychartdir没有导入&#xff0c;脚本执行报错。以下是报错内容&#xff1a;[modpsLGJF-ZYC5-MMSC-WEB02 ~]$ python /opt/aspire/product/modps/mopps/shell/dayreport_linux.py/etc/host.conf: line 1: bad command nospoof on"Tr…

vim 中Ctags的安装和使用

Ctags是一个用来为源文件中的标识符&#xff08;如变量、函数、类成员、宏定义等&#xff09;创建索引文件的程序。这些tags文件能被编辑器或其它工具用来快速查找定位源代码中的符号&#xff08;tag/symbol&#xff09;&#xff0c;如变量名&#xff0c;函数名等。 Tags文件中…

Java并发编程之AbstractQueuedSynchronizer(AQS)源码解析

自己一个人随便看看源码学习的心得&#xff0c;分享一下啦&#xff0c;不过我觉得还是建议去买本Java并发编程的书来看会比较好点&#xff0c;毕竟个人的理解有限嘛。 独占锁和共享锁 首先先引入这两个锁的概念&#xff1a;独占锁即同一时刻只有一个线程才能获取到锁&#xf…

采集用python还是火车头_我才知道爬虫也可以酱紫--火车采集器

我才知道爬虫还可以这样—火车采集器的使用说在前面额。。。好吧&#xff0c;我这一个三毛钱的屌丝也开始步入实习阶段了&#xff0c;在北京其实也挺好的&#xff0c;虽说压力大&#xff0c;但是今后就业机会也相对而言大一些。好了&#xff0c;说回今天的主题&#xff0c;之前…

mvn 使用中的错误

出现这种错误的时候&#xff1a;mvn Error building POM may not be this projects POM&#xff0c;报的是那个jar 包&#xff0c;就删除那个jar 包&#xff0c;重新mvn clean install .ok

Java并发编程之FutureTask源码解析

上次总结一下AQS的一些相关知识&#xff0c;这次总结了一下FutureTask的东西&#xff0c;相对于AQS来说简单好多呀 之前提到过一个LockSupport的工具类&#xff0c;也了解一下这个工具类的用法&#xff0c;这里也巩固一下吧 /*** Makes available the permit for the given th…

java 删除二维数组中的null_避免在Java中检查Null语句

1.概述通常&#xff0c;在Java代码中处理null变量、引用和集合很棘手。它们不仅难以识别&#xff0c;而且处理起来也很复杂。事实上&#xff0c;在编译时无法识别处理null的任何错误&#xff0c;会导致运行时NullPointerException。在本教程中&#xff0c;我们将了解在Java中检…

Java并发编程之并发容器ConcurrentHashMap(JDK1.7)解析

最近看了一下ConcurrentHashMap的相关代码&#xff0c;感觉JDK1.7和JDK1.8差别挺大的&#xff0c;这次先看下JDK1.7是怎么实现的吧 哈希&#xff08;hash&#xff09; 先了解一下啥是哈希&#xff08;网上有很多介绍&#xff09;&#xff0c;是一种散列函数&#xff0c;简单来…

带控制端的逻辑运算电路_分别完成正整数的平方、立方和阶乘的运算verilog语言...

练习&#xff1a;设计一个带控制端的逻辑运算电路&#xff0c;分别完成正整数的平方、立方和阶乘的运算。 //--------------myfunction---------- modulemyfunction(clk,n,result,reset,sl); output[6:0]result; input[2:0] n; input reset,clk; input [1:0] sl; reg[6:0]resul…

Java并发编程之并发容器ConcurrentHashMap(JDK1.8)解析

这个版本ConcurrentHashMap难度提升了很多&#xff0c;就简单的谈一下常用的方法就好了&#xff0c;可能有些讲的不太清楚&#xff0c;麻烦发现的大佬指正一下 主要数据结构 1.8将Segment取消了&#xff0c;保留了table数组的形式&#xff0c;但是不在以HashEntry纯链表的形式…

simulink显示多个数据_如何在 Simulink 中使用 PID Tuner 进行 PID 调参?

作者 | 安布奇责编 | 胡雪蕊出品 | CSDN(ID: CSDNnews)本文为一篇技术干货&#xff0c;主要讲述在Simulink如何使用PID Tuner进行PID调参。PID调参器( PIDTuner)概述1.1 简介使用PID Tuner可以对Simulink模型中的PID控制器&#xff0c;离散PID控制器&#xff0c;两自由度PID控制…

Java并发编程之堵塞队列介绍以及SkipList(跳表)

堵塞队列 先了解一下生产者消费者模式&#xff1a; 生产者就是生产数据的一方&#xff0c;消费者就是消费数据的另一方。在多线程开发中&#xff0c;如果生产者处理速度很快&#xff0c;而消费者处理速度很慢&#xff0c;那么生产者就必须等待消费者处理完&#xff0c;才能继…

python生成list的时候 可以用lamda也可以不用_python 可迭代对象,迭代器和生成器,lambda表达式...

分页查找#5.随意写一个20行以上的文件(divmod)# 运行程序&#xff0c;先将内容读到内存中&#xff0c;用列表存储。# l []# 提示&#xff1a;一共有多少页# 接收用户输入页码&#xff0c;每页5条&#xff0c;仅输出当页的内容def read_page(bk_list,n,endlineNone):startline …

数据挖掘技术简介[转]

关键词&#xff1a; 关键词&#xff1a;数据挖掘 数据集合 1. 引言  数据挖掘(Data Mining)是从大量的、不完全的、有噪声的、模糊的、随机的数据中提取隐含在其中的、人们事先不知道的、但又是潜在有用的信息和知识的过程。随…

树莓派安装smbus_树莓派使用smbus不兼容问题(no module named 'smbus')

树莓派使用smbus不兼容问题(no module named ‘smbus’)python3.5–3.6可以使用smbus2代替smbus1. 先参考以下方法&#xff1a;github讨论树莓派社区2.Pypi上可以下载smbus2smbus2PyPi介绍&#xff1a;当前支持的功能有&#xff1a;获取i2c功能(I2C_FUNCS)read_bytewrite_byter…

Java并发编程之线程池ThreadPoolExecutor解析

线程池存在的意义 平常使用线程即new Thread()然后调用start()方法去启动这个线程&#xff0c;但是在频繁的业务情况下如果在生产环境大量的创建Thread对象是则会浪费资源&#xff0c;不仅增加GC回收压力&#xff0c;并且还浪费了时间&#xff0c;创建线程是需要花时间的&…

面向过程的门面模式

{*******************************************************}{ }{ 业务逻辑一 }{ }{ 版权所有 (C) 2008 陈…