在Java多线程中,i++和i--是非线程安全的。例子:public class PlusPlusTest { public static void main(String[] args) throws InterruptedException { Num num = new Num(); ThreadA threadA = new ThreadA(num); ThreadB threadB = new ThreadB(num); threadA.start(); threadB.start(); Thread.sleep(200); System.out.println(num.count); }}class ThreadA extends Thread { private Num num; public ThreadA(Num num) { this.num = num; } @Override public void run() { for (int i = 0; i < 1000; i++) { num.count++; } }}class ThreadB extends Thread { private Num num; public ThreadB(Num num) { this.num = num; } @Override public void run() { for (int i = 0; i < 1000; i++) { num.count++; } }}class Num { int count = 0; public Num() { }}以上代码输出结果基本上不是2000,会比2000小。原因:在线程A中,i++的过程为:temp1 = i; temp2 = temp1 + 1; i = temp2;在线程B中,i++的过程为:temp3 = i; temp4 = temp3 + 1; i = temp4;在i=0的时候,线程A和B同时读取i=0。线程A执行++后,i被修改成1。线程B执行++后,i被修改,但还是1。问:这样的解释对么?想到把count变量申明为volatile,但是:即使把count申明为volatile,输出的结果也不是2000,请问为什么?class Num { volatile int count = 0; public Num() { }}最后把count变量包装成AtomicInteger之后,输出的结果为2000,正确,这又是为什么?
4 回答

慕的地8271018
TA贡献1796条经验 获得超4个赞
volatile只能保证可见性,即别人修改了之后你立马能读到,但是你改的时候别人也可以改。
AtomicInteger是基于CAS(Compare And Swap)的。
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。 两个问题: (1)CAS算法仍然可能会出现冲突,例如A、B两个线程,A已经进入写内存但未完成,此时A读取到的副本且读取成功,AB两个线程同时进入写内存操作,必然会造成冲突。 CAS算法本质并非完全无锁,而是把获得锁和释放锁推迟至CPU原语实现,相当于尽可能的缩小了锁的范围;直接互斥地实现系统状态的改变,它的使用基本思想是copy-on-write——在修改完对象的副本之后再用CAS操作将副本替换为正本。 (2)ABA问题,若其中一个线程修改A->B->A,另外一个线程仍然读取到A,虽然值是预期值,但并不能说明该内存值没有变化。

慕桂英4014372
TA贡献1871条经验 获得超13个赞
添加回答
举报
0/150
提交
取消