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

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

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并发编程的艺术》

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值