volatile无法保证i++原子性的解决方案

来源:互联网 发布:美国 制造业回归 知乎 编辑:程序博客网 时间:2024/05/22 10:37

一、阻塞算法与非阻塞算法

1、阻塞算法

以常见的同步实现方式synchronized为例,同一时间段,同一个锁,只能有一个线程获得,其他未获取到的线程阻塞,直到拥有锁的线程释放锁。

下图演示了一个阻塞算法保证一个共享数据结构的行为:

这里写图片描述

2、非阻塞算法

线程A请求某种操作,如果系统无法响应;则通知A线程,A可先去执行其他操作;

下图演示了一个非阻塞算法保证一个共享数据结构的行为:

这里写图片描述

二、Volatile 变量存在的问题,

Volatile变量写的内存语意:

当写一个Volatile变量时,JMM会把线程对应的本地内存中的共享变量值刷新到主内存;

Volatile变量读的内存语意:

当读一个Volatile变量时,JMM会把该线程对应的本地内存置为无效。重新主内存中读取共享变量;

还有一点需要注意:

对于任意单个volatile变量的读写具有原子性,但类似于volatile++这种复合操作不具有原子性;


public class SingleWriterCounter{    private volatile long count = 0;     public void inc(){         this.count++;     }      public long count(){          return this.count;      }}

例如上面这段代码,线程A和线程B分别执行100此inc方法,最终count的值可能为多少?

最小值2的解解释:

①A线程执行第99次inc方法,但是未写入主内存,此时主内存中count的值任为0;
②此时B线程执行第1次inc方法,从主内存中获取到的值为0
③A线程将执行第99次inc方法的值写入主内存,此时主内存中的count值为99;B线程将执行第1次inc方法的结果写入主内存,此时主内存的count值为1
④线程A执行第100次inc方法,从主内存中获取到的值为1 ⑤线程B执行继续执行到第100次,并将100写入主内存;
⑥线程A将第100次inc方法结果2写入主内容

最大值200的解释:①A线程执行100次并将结果100写入主内存;②B线程执行100次并将结果200写入主内存;

三、Volatile 问题的解决方案

解决方案一:阻塞算法

public class SynchronizedCounter{    long count = 0;    public void inc(){        synchronized(this){            count++;        }    }    public long count(){        synchronized(this){            return this.count;        }    }}

解决方案二:非阻塞算法

import java.util.concurrent.atomic.AtomicLong;public class AtomicLong{    private AtomicLong count = new AtomicLong(0);    public void inc(){        boolean updated = false;        while(!updated){            long prevCount = this.count.get();            updated = this.count.compareAndSet(prevCount, prevCount + 1);        }    }    public long count(){        return this.count.get();    }}

现代CPU广泛支持一种对内存中的共享数据进行操作的一种特殊指令(CAS),该指令的语意如下:简单的来说,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V

CAS是一个原子操作,实现如下:

 /**     * Atomically sets the value to the given updated value     * if the current value {@code ==} the expected value.     *     * @param expect the expected value     * @param update the new value     * @return {@code true} if successful. False return indicates that     * the actual value was not equal to the expected value.     */    public final boolean compareAndSet(long expect, long update) {        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);    }

非阻塞的解决方案的通俗解释如下:

小王去买奶茶,但是有很多人都在卖,此时小王有两种解决方案:

① 排队等待(阻塞算法)
② 过段时间再来买(非阻塞算法)

关于CAS的ABA问题,请参考:http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html

参考如下:

1、非阻塞算法 并发编网
2、《Java并发编程的艺术》

阅读全文
1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 起亚k3钥匙没电怎么办 汽车摇控钥匙失灵了怎么办 逍客钥匙没有电怎么办 车钥匙芯片坏了怎么办 车钥匙密码丢了怎么办 遥控车钥匙丢了怎么办 车备用钥匙丢了怎么办 2把车钥匙丢了怎么办 汽车遥控钥匙丢了怎么办 车钥匙忘在车里怎么办 轩逸钥匙没电了怎么办 车钥匙锁后备箱怎么办 死飞车掉漆了怎么办 被吸血蝙蝠咬了怎么办 pos失败钱扣了怎么办 现金支票写错了怎么办 公路上汽车坏了怎么办 驾图盒子绑定过怎么办 车在路上没油了怎么办 狂野飙车8闪退怎么办 宜家会员卡没带怎么办 飞猪机票买贵了怎么办 发现机票买贵了怎么办 埋线双眼皮松了怎么办 吸气时左胸口疼怎么办 兰芝隔离有气泡怎么办 19岁眼部有皱纹怎么办 三星s4开不了机怎么办 三星手机变砖了怎么办 面霜一次用多了怎么办 午觉睡多了头疼怎么办 下午睡多了头疼怎么办 白天睡久了头疼怎么办 睡觉睡多了头痛怎么办 泡菜坛子水少了怎么办 咸菜坛子生花了怎么办 四川泡菜生花了怎么办 三岁宝宝说话不清楚怎么办 电脑读不出来u盘怎么办 跳绳的时候漏尿怎么办 喝完酒一直想吐怎么办