在java并发编程中,如果想要保证程序的线程安全,就要保证代码的原子性,可见性,有序性
Java本身的原子性
由java内存模型来直接保证具有原子性变量操作的有 read/load/use/assign/store/write.
java提供了原子性的技术保障有如下:
1.synchronized(互斥锁)
2.Lock(互斥锁)
3.原子类(CAS)
以上两个都是通过互斥锁实现,即同一时刻只允许一个线程来操作该变量,保障了原子性. 上两种也被称为悲观锁 CAS为乐观锁
AtomicInteger 以原子的方式更新int值
public AtomicInteger() 创建具有初始值 0 的新AtomicInteger.
public AtomicInteger(int i) 创建具有给定初始值的新 AtomicInteger.
方法
int getAndIncrement() 以原子方式将当前值加1 i++.
int incrementAndGet() 以原子方式将当前值加1 ++i.
int getAndDecrement() 以原子方式将当前值减1 i--.
int decrementAndGet() 以原子方式将当前值减1 --i.
int get() 获取当前值
int intValue() 获取当前值
int getAndAdd(int delta) 以原子方式将给定值与当前值相加 先获取,加相加 与 i++相似
int addAndGet(int delta) 以原子方式将给定值与当前值相加 先相加在获取 与++i相似
原理
CAS无锁机制 比较并替换
当多条线程尝试使用CAS同时更新同一个变量时,只有其中一条线程能更新变量的值,而其他线程都失败,失败的线程并不会被挂起,而是告知这次竞争失败,并可以再次尝试.
缺点
循环时间长开销很大。
CAS 通常是配合无限循环一起使用的,如果 CAS 失败,会一直进行尝试。如果 CAS 长时间一直不成功,可能会给 CPU 带来很大的开销。只能保证一个变量的原子操作。
当对一个变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个变量操作时,CAS 目前无法直接保证操作的原子性。ABA问题。
第一条线程获取到V位置的值 假设是 1 第二条线程获取到V位置的值 也是1 第一条线程cas成功 将值改为 0 第一条线程又cas成功 将值改回 1 这时第二条线程cas 发现值没变 还是1 cas成功 实际上当第二条线程cas时 V位置的值已经从 1-0-1 这就是ABA问题 如何解决 每次获取V位置的值时,带上一个版本号.这样就可以避免ABA问题 java中AtomicStampedReference这个类在cas时就是通过版本号来解决的