基于CAS
下面就以AtomicInteger为例。
2、AtomicInteger在没有AtomicInteger之前,对于一个Integer的线程安全操作,是需要使用同步锁来实现的,当然现在也可以通过ReentrantLock来实现,但是最好最方便的实现方式是采用AtomicInteger。
具体示例:
package com.collection.test;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 原子类的测试
*/
public class AtomicTest {
private static AtomicInteger atomicInteger = new AtomicInteger();
//获取当前值
public static void getCurrentValue(){
System.out.println(atomicInteger.get());//-->0
}
//设置value值
public static void setValue(){
atomicInteger.set(12);//直接用12覆盖旧值
System.out.println(atomicInteger.get());//-->12
}
//根据方法名称getAndSet就知道先get,则最后返回的就是旧值,如果get在后,就是返回新值
public static void getAndSet(){
System.out.println(atomicInteger.getAndSet(15));//-->12
}
public static void getAndIncrement(){
System.out.println(atomicInteger.getAndIncrement());//-->15
}
public static void getAndDecrement(){
System.out.println(atomicInteger.getAndDecrement());//-->16
}
public static void getAndAdd(){
System.out.println(atomicInteger.getAndAdd(10));//-->15
}
public static void incrementAndGet(){
System.out.println(atomicInteger.incrementAndGet());//-->26
}
public static void decrementAndGet(){
System.out.println(atomicInteger.decrementAndGet());//-->25
}
public static void addAndGet(){
System.out.println(atomicInteger.addAndGet(20));//-->45
}
public static void main(String[] args) {
AtomicTest test = new AtomicTest();
test.getCurrentValue();
test.setValue();
//返回旧值系列
test.getAndSet();
test.getAndIncrement();
test.getAndDecrement();
test.getAndAdd();
//返回新值系列
test.incrementAndGet();
test.decrementAndGet();
test.addAndGet();
}
}
源代码:
private volatile int value;// 初始化值
/**
* 创建一个AtomicInteger,初始值value为initialValue
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
/**
* 创建一个AtomicInteger,初始值value为0
*/
public AtomicInteger() {
}
/**
* 返回value
*/
public final int get() {
return value;
}
/**
* 为value设值(基于value),而其他操作是基于旧值<--get()
*/
public final void set(int newValue) {
value = newValue;
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* 基于CAS为旧值设定新值,采用无限循环,直到设置成功为止
*
* @return 返回旧值
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();// 获取当前值(旧值)
if (compareAndSet(current, newValue))// CAS新值替代旧值
return current;// 返回旧值
}
}
/**
* 当前值+1,采用无限循环,直到+1成功为止
* @return the previous value 返回旧值
*/
public final int getAndIncrement() {
for (;;) {
int current = get();//获取当前值
int next = current + 1;//当前值+1
if (compareAndSet(current, next))//基于CAS赋值
return current;
}
}
/**
* 当前值-1,采用无限循环,直到-1成功为止
* @return the previous value 返回旧值
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* 当前值+delta,采用无限循环,直到+delta成功为止
* @return the previous value 返回旧值
*/
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
}
/**
* 当前值+1, 采用无限循环,直到+1成功为止
* @return the updated value 返回新值
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;//返回新值
}
}
/**
* 当前值-1, 采用无限循环,直到-1成功为止
* @return the updated value 返回新值
*/
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;//返回新值
}
}
/**
* 当前值+delta,采用无限循环,直到+delta成功为止
* @return the updated value 返回新值
*/
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;//返回新值
}
}
/**
* 获取当前值
*/
public int intValue() {
return get();
}
说明:使用与源代码都简单到爆了!自己看看注释。
注意:
-
value是volatile的。
-
单步操作:例如set()是直接对value进行操作的,不需要CAS,因为单步操作就是原子操作。
-
多步操作:例如getAndSet(int newValue)是两步操作-->先获取值,在设置值,所以需要原子化,这里采用CAS实现。
-
对于方法是返回旧值还是新值,直接看方法是以get开头(返回旧值)还是get结尾(返回新值)就好
-
CAS:比较CPU内存上的值是不是当前值current,如果是就换成新值update,如果不是,说明获取值之后到设置值之前,该值已经被别人先一步设置过了,此时如果自己再设置值的话,需要在别人修改后的值的基础上去操作,否则就会覆盖别人的修改,所以这个时候会直接返回false,再进行无限循环,重新获取当前值,然后再基于CAS进行加减操作。
-
如果还是不懂CAS,类比数据库的乐观锁。
补充一个东西:
// 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); }
}
private volatile int value;
这是AtomicInteger的所有属性,其中value存的是当前值,而当前值存放的内存地址可以通过valueOffset来确定。实际上是“value字段相对Java对象的起始地址的偏移量”
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
CAS方法:通过对比“valueOffset上的value”与expect是否相同,来决定是否修改value值为update值。
ABA 问题:执行流程如下
int i=100;
Thread1: 100->101->100
Thread2: 100-> 200
Thread2没有发现Thread1的中间i发生了变化的过程。
ABA问题怎么解决?
第一种姿势:不处理,对于只关注最后的结果,不关注中间变化过程,ABA 无所谓,例如 AtomicInteger.compareAndset(...)
第二种姿势:使用版本号控制,eg. AtomicStampedReference 提供一个版本号,过程如下
Thread1: 100 1->101 2->100 3(前边为数据,空格后是版本号)
Thread2: 100 1-> 200(此时报错,版本号不一致)
那版本号是否也会出现ABA问题,只要后续的版本号不会出现之前的版本号就行(eg. 递增)