直接看代码:
按理来说是要打印 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 指令。
当 int 取值-1~5时,JVM采用iconst指令将常量压入栈中。
这就是 没有达到 i = 60000 的原因。
注意了 t1 是先读的, t1读完 t2 进来了 t2 也是读了 i 的值 ,所以就不存在先写后读。也就不存在可见性问题,volatile是写完之后,然后再读。
那如何解决呢?
方法1、加sync关键字,也可以把关键字加在Counter类里的add方法上,这样就完美的解决了 i = 60000
方法2、用可重入锁也可以
方法3、使用原子类
假设用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实现的。
跟一下源码
如果您发现该资源为电子书等存在侵权的资源或对该资源描述不正确等,可点击“私信”按钮向作者进行反馈;如作者无回复可进行平台仲裁,我们会在第一时间进行处理!
加入交流群
请使用微信扫一扫!