java并发和高并发之 线程安全性——原子性-atomic-1

46 篇文章 0 订阅
12 篇文章 0 订阅

一、线程安全性

1、基本概念:

》定义:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进行将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

2、线程安全性的几个表现点:

》原子性:提供了互斥访问,同一时刻只能有一个线程来对它进行操作;

》可见性:一个线程对主内存的操作可以及时地被其他线程观察到;

》有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序;

3、原子性 ——jdbc中的 Atomic包:

》AtomicXXX:CAS 、Unsafe.compareAndSwapInt

》AtomicLong、LongAdd

》AtomicReference、AtomicReferenceFieldUpdater

》AtomicStampReference: CAS的ABA问题

4、Atomic包实例分析

1)AtomicXXX:CAS 、Unsafe.compareAndSwapInt

如下为对原来的累加计数实例进行原子性优化后的代码:

优化前的代码:

优化后的代码:

 

注意上方这里的Add方法的差异,以及add里面用到了count,注意其上方count 定义的差异。

源码分析:

incrementAndGet()方法中,用到了unsafe 类的getAddInt 方法,主要的是该方法的实现是关键。该实现的原理为,在其中使用了一个do  while 循环,而while的循环条件中,用到了this.compareAndSwapInt(var1,var2,var5,var5 +var4); 

 getAndInt () 方法中,第一个参数var1 是个对象,表示调用者,如上即为count ,第二个值是当前的值,比如要执行2+1 ,则第二个值就是2,第三个值就是1。而compareAndSwapInt 是个native修饰的java底层的方法。compareAndSwapInt 方法中的几个参数,含义为,调用对象 比如此处count,当前值var2 和从底层获取的值 var5如果相等,则返回var5+ var4

CompareAndSwapXXX 也就是CAS的名称由来。

扩展:Atomic包中还有个AtomicBoolean类,该类常用于如下情形:希望某个方法只执行一次甚至当前只能有一个线程执行这段代码,执行前有个标志变量,该变量在某个方法执行前为false,执行后变为true,此时使用其中compareAndSet(boolean expect,boolean update)方法非常合适。

2)

将上方的AtomicInteger 改为AtomicLong 类,其他不用动,进行测试

 

 

 如上为AtomicLong 类的使用,如上所示,只是将原来的AtomicInteger换成了AtomicLong ,其他都没变。

如下为使用jdk8中新的LongAdder类的使用,此处除了修改count的类型定义,

也根据LongAdder 的对应方法.

扩展:对于普通的Long和Double类型变量,jvm允许将64位的读操作或写操作,拆成2个32位的读操作或者写操作进行计算。LongAdder的实现原理:将热点数据分离,比如将AtomicLong的内部核心数据value分离成一个数组,每个线程好运时,通过hash等算法映射到其中一个数字进行计数,而最终的计数结果,则为这个数组的求和累加。其中热点数据value会被分离成多个单元的cell,每个cell独自维护内部的值,当前对象的实际值,由所有的cell累计合成。这样热点就进行了有效的分离,并提高了并行度。LongAdder是在AtocmicXXX的基础上,将单点的更新压力分散到各个节点上。在低并发时,通过对base的直接更新,可以很好地保证和atomic的性能基本一致。在高并发时,通过分散提高了性能。LongAdder也有自己的缺陷,缺点是在某些时,如果有并发更新,可能会导致统计的数据由误差。所以在实际使用中,如果高并发时,可以使用LongAdder。在低并发时,使用Atomic更简单直接高效准确。但对于序列号生成等特别准确时,适合AtomicLong,而不是LongAdder

 

 

 3)AtomicReference、AtomicReferenceFieldUpdater

先看示例:

 

 执行结果为何是4呢?原因如下:

下面展示第二个类的实例:

这个类,核心是向原子性去更新某一个类的实例的某个被volatile修饰的但一定不能被static修饰的一个字段,比如上方的实例中,更新example5,的一个被volatile修饰但不被static修饰的字段count。本质就是对原子性的变量进行修改。

如上的实例含义为:第一个if时,判断count值是否为100,如果是,则更新为120.因为满足条件,所以执行,结果count变为120.当走到第二个if时,做同样的判断,但此时count为120,不符合条件,所以执行else语句。

4)AtomicStampReference: 核心是要解决CAS的ABA问题。

ABA 问题指的是,在CAS操作的时候,其他线程将A的值改为了B,但是又改回了A,本线程使用期望值A与当前变量进行比较时,发现A变量没有变,于是CAS就将A进行了交换操作,实际上该值已经被其他线程改变过,这与设计思想不符合。因此ABA问题的解决思路就是每次更新时,将变量的版本号加1,那么之前的A改为B 再改为A,就有了一个版本号的变化。因此,某个变量一旦被某个线程修改过,就会有版本记录,从而解决ABA问题。实质用的是可见性原理。

该类AtomicStampReference中的关键方法为compareAndSet()方法。此处不再举例。

再介绍一个类:AtomicLongArray  也是类似的。

此处再用示例介绍下AtomicBoolean类。。

 

执行结果:

 如上方法只会执行一次,因为test()方法中的判断,当执行过一次后,isHappened.compareAndSet(false,true)会将isHappened变量置为true,从而不符合执行后面循环的条件,而只是执行一次,绝对不会重复。

如果遇到期待某段逻辑只是执行一次,可仿照上例。

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值