关于Java中的Atomic

57 篇文章 0 订阅

为什么会出现Atomic类

在多线程或者并发环境中,我们常常会遇到这种情况 int i=0; i++ 但这种写法是线程不安全的。
为了达到线程安全的目的,我们通常会用synchronized来修饰对应的代码块。还有一种办法就是使用J.U.C包下的atomic类
Atomic类的原理是什么呢
atomic类是通过自旋CAS操作volatile变量实现的

CAS(O,V,A)

V:主内存存放的实际变量值
O:当前线程认为主内存的变量值
A:希望将变量替换的值
主内存:num= 1;—>经过线程一变成0
线程1: num= 0; cas(1,1,0) O=V,认为此时没有线程修改主内存值,此时将0,更新到主内存
线程2: num= 0; cas(1,0,0) O!=V,认为已经有线程修改了这个值,此时修改失败,返回主内存的最新值O,再次重试

使用volatile修饰变量是为了多个线程间变量的值能及时同步。也就是所谓的可见性,是指当一条线程修改了共享变量的值,新值对于其他线程来说是可以立即得知的

为什么使用Atomic类

因为volatile虽然比synchronized性能要好一些,但是由于例如i++这种操作不符合原子性,而是个复合操作。我们可以简单讲这个操作理解为由这三步组成:

1.读取
2.加一
3.赋值

所以,在多线程环境下,有可能有线程将num读取到本地内存中,此时其他线程可能已经将num增大了很多,当前线程依然对过期的num进行自加,重新写到主存中,最终导致了num的结果不合预期
所以此时可以使用java并发包中的原子操作类原子操作类是通过循环CAS的方式来保证其原子性的。

ABA问题:
 对于一个旧的变量值A,线程2将A的值改成B又改成可A,此时线程1通过CAS看到A并没有变化,但实际A已经发生了变化,这就是ABA问题。解决这个问题的方法很简单,记录一下变量的版本就可以了,在变量的值发生变化时对应的版本也做出相应的变化,然后CAS操作时比较一下版本就知道变量有没有发生变化。
atomic包下AtomicStampedReference类实现了这种思路。Mysql中Innodb的多版本并发锁也是这个原理。
CAS机制会产生ABA问题:
num = 0;—> 1—>0(线程1线程2串行,线程1线程3并行)
线程1:cas(0,0,1) 1替换成主内存
线程2:cas(1,1,0) 1替换成了0
线程3:cas(0,0,5) 0替换成了5
产生了线程二的数据脏读,避免思路:可以添加一个版本号,即修改的次数,判断当前值到底有没有被改

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值