Java中的原子操作类详解
Java中的原子操作类详解
1、原子更新基本类型类 2、原子更新数组 3、原子更新引用类型 4、原子更新字段类 5、CAS算法 6、ABA问题
当更新一个变量时,如果多线程同时更新这个变量,为了保证多线程不会更新这个变量,一般使用关键字synchronized来保证多线程不会同时修改该变量。
jdk1.5后,java.util.concurrent.atomi包下提供了更简洁,高效和安全的原子操作类:
1、原子更新基本类型类
-
AtomicInteger:整型原子类
-
AtomicLong:长整型原子类
-
AtomicBoolean :布尔型原子类
-
AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免synchronized 的高开销,执行效率大为提升。
-
CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。UnSafe 类的objectFieldOffset() 方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址。另外 value 是一个volatile变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。
-
源码:
// setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
-
例子
/**
* 多线程环境不使用原子类保证线程安全(基本数据类型)
* @author T-lih
*/
public class AtomicDemo1 {
private volatile int count = 0;
//若要线程安全执行执行count++,需要加锁
public synchronized void incrementAdd() {
count++;
}
public int getCount() {
return count;
}
}
/**
* 多线程环境使用原子类保证线程安全(基本数据类型)
*/
public class AtomicDemo2 {
private AtomicInteger count = new AtomicInteger();
public void increment() {
count.incrementAndGet();
}
//使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
public int getCount() {
return count.get();
}
}
2、原子更新数组
-
AtomicIntegerArray:整型数组原子类
-
AtomicLongArray:长整型数组原子类
-
AtomicReferenceArray :引用类型数组原子类
3、原子更新引用类型
-
AtomicReference:引用类型原子类
-
AtomicReferenceFieldUpdater:原子更新引用类型里的字段
-
AtomicMarkableReference :原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来
4、原子更新字段类
-
AtomicIntegerFieldUpdater:原子更新整型字段的更新器
-
AtomicLongFieldUpdater:原子更新长整型字段的更新器
-
AtomicStampedReference :原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
5、CAS算法
即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数
-
需要读写的内存值 V
-
进行比较的值 A
-
拟写入的新值 B
当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。
6、ABA问题
如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。
JDK 1.5 以后的 AtomicStampedReference 类
就提供了此种能力,其中的 compareAndSet 方法
就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
本学习要点,相关原子操作类方法使用。
来源:oschina
链接:https://my.oschina.net/lihua20103181/blog/3171005