多线程(二十):原子性CAS机制


prtyaa
prtyaa 2023-12-28 16:48:17 50013 赞同 0 反对 0
分类: 资源
怎么获取线程的执行结果

直接看代码:

按理来说是要打印 i 的值为60000的。但是预期没有达到。给 i 加volatile试试?

会发现即使加了volatile也是没用的。

这就是原子性问题,add方法是i++ ,但是i++ 不是一个原子操作。同理 i = i+1 也不是原子操作,而即便是用volatile修饰之后,也不能保证原子性。volatile只是保证了可见性,但不保证原子性。

volatile是一个线程写,另一个线程读的时候,保证了可见性,就是写完的时候,可以读的到

现在看一下for循环里面new 一个线程 然后start(), start了之后,并不一定执行吧,主线程继续执行for循环,反编译之后

getfield是去堆内存中取 i 的值。代码中默认值 i = 0,取了 i 之后放到操作数栈(入栈)

当int取值-1~5采用 iconst 指令,取值-128~127采用bipush指令,取值-32768~32767采用sipush指令,取值-2147483648~2147483647采用 ldc 指令。

iconst

当 int 取值-1~5时,JVM采用iconst指令将常量压入栈中。

iadd 加法指令 把0,1 出栈 ,然后运算 0+1 =1 ,再把结果入栈

 

putfiled 把栈顶的元素赋值给实例字段 i(目前栈里面就一个 1 ,所以这个元素就是栈顶了), i 就变成了1 。

假设t2比t1后执行,当t1 getfield的值为0 ,然后加1 然后再放进i的时候,也就是add 或者put的时候,t2执行了,此时t2读到的是 i = 0吧 ,然后t2 也进行iconst,iadd,这时候t1已经getfield完毕了 i 变成了 1, 然后t2 接着getfield完毕,t2的 put i也是 1 , 两个线程 i 都是 1

这就是 没有达到 i = 60000 的原因。

注意了 t1 是先读的, t1读完 t2 进来了 t2 也是读了 i 的值 ,所以就不存在先写后读。也就不存在可见性问题,volatile是写完之后,然后再读。

那如何解决呢?

方法1、加sync关键字,也可以把关键字加在Counter类里的add方法上,这样就完美的解决了 i = 60000

方法2、用可重入锁也可以

方法3、使用原子类


CAS

假设用CAS 看刚才的t1,t2线程的话 刚开始i = 0

当t1 线程CAS(0,1) t2 CAS(0,1) t1 put的时候 CAS(0,1) 中的0 跟类里的 i 比较一下0=0 然后把 i的值修改为1, 当t2 put的时候 t2 CAS(0,1) 0和i比较i的值为1了,0不等于1,所以t2不能够修改

CAS就是compare and swap 的缩写。比较和替换。

 

手动实现一个cas, Unsafe工具类非常强大

Atomic原子类底层就是CAS实现的。

跟一下源码


如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!

评价 0 条
prtyaaL2
粉丝 1 资源 1949 + 关注 私信
最近热门资源
银河麒麟桌面操作系统备份用户数据  123
统信桌面专业版【全盘安装UOS系统】介绍  116
银河麒麟桌面操作系统安装佳能打印机驱动方法  108
银河麒麟桌面操作系统 V10-SP1用户密码修改  101
最近下载排行榜
银河麒麟桌面操作系统备份用户数据 0
统信桌面专业版【全盘安装UOS系统】介绍 0
银河麒麟桌面操作系统安装佳能打印机驱动方法 0
银河麒麟桌面操作系统 V10-SP1用户密码修改 0
作者收入月榜
1

prtyaa 收益393.62元

2

zlj141319 收益217.55元

3

1843880570 收益214.2元

4

IT-feng 收益208.98元

5

风晓 收益208.24元

6

777 收益172.71元

7

Fhawking 收益106.6元

8

信创来了 收益105.84元

9

克里斯蒂亚诺诺 收益91.08元

10

技术-小陈 收益79.5元

请使用微信扫码

加入交流群

请使用微信扫一扫!