Java并发编程的艺术-学习笔记-7

Java中的原子操作类

原子更新基本类型

包括以下3个类:
AtomicBoolean、AtomicInteger和AtomicLong

以上三个类提供的方法几乎一模一样,以AtomicInteger为例展示。

方法 描述
int addAndGet(int delta) 以原子的方式将输入的数值与AtomicInteger里的value相加,并返回结果
boolean compareAndSet(int expect, int update) 如果输入数值等于预期,则以原子方式更新数值,并返回true;否则不更新并返回false
int getAndIncrement() 以原子方式将当前值增加1,返回自增前的旧值
int getAndSet(int newValue) 以原子方式设置为newValue的值,并返回旧值
void LazySet(int newValue) 最终会被设置为newValue,但是其他线程可能在一段时间内还是访问到旧值

getAndIncrement()的实现源码其实就是CAS

1
2
3
4
5
6
7
8
9
10
11
12
13
public final int getAndIncrement(){
for( ; ; ){
int current = get();
int next = current + 1;
if(compareAndSet(current, next)){
return current;
}
}
}
public final boolean compareAndSet(int expect, int update){
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

Unsafe.java的CAS方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final native boolean compareAndSwapObject(Object o,
long offset,
Object expected,
Object update);
public final native boolean compareAndSwapInt(Object o,
long offset,
int expected,
int update);
public final native boolean compareAndSwapLong(Object o,
long offset,
long expected,
long update);

在Unsafe中只提供了三种CAS方法,对于char、float和double变量我们可以先将其转换成整形或者长整形,再进行CAS。
其实AtomicBoolean也是先将Boolean转成整形,再使用compareAndSwapInt进行CAS。

原子更新数组

包括以下3个类:
AtomicIntegerArray:原子更新整形数组里的元素
AtomicLongArray:原子更新长整形数组里的元素
AtomicReferencey:原子更新引用类型数组里的元素

方法 描述
int addAndGet(int i, int delta) 以原子方式将输入值与数组索引i的元素相加,并返回旧值
boolean compareAndSet(int i, int expect, int update) CAS设置数组索引i的元素

使用示例:

1
2
3
4
5
6
7
8
9
10
static int []value = new int[]{1, 2,};
//AtomicIntegerArray内部的数组是value数组的拷贝
static AtomicIntegerArray array = new AtomicIntegerArray(value);
public static void main(String[] args){
System.out.println(array.getAndSet(0, 11)); //输出1
System.out.println(array.get(0)); //输入11
System.out.println(value[0]); //输出1
}

原子更新引用类型

包括以下3个类:
AtomicReference:原子更新引用类型
AtomicReferenceFieldUpdater:原子更新引用类型的字段
AtomicMarkableReference:原子更新带有标记位的引用类型。构造方法是AtomicMrkableReference(V initialRef, booleanMark)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static AtomicReference<User> reference1 = new AtomicReference<>();
static class User{
boolean flag;
int i;
String s;
public User(boolean flag, int i, String s){
this.flag = flag;
this.i = i;
this.s = s;
}
public boolean getFlag(){ return flag; }
public int getInt(){ return i; }
public String getSring(){ return s; }
public String toString(){return "flag: " + flag + ", i: " + i + ", s: " + s;}
}
public static void main(String[] args){
User user = new User(true, 11, "Irving");
User newUser = new User(false, 2, "Kawhi");
reference1.set(user);
reference1.compareAndSet(user, newUser);
System.out.println(reference1.toString()); //输出: flag: false, i: 2, s: Kawhi

原子更新字段

包括以下3个类:
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器
AtomicLongFieldUpdater:原子更新长整型字段的更新器
AtomicStampedReference:原子更新带有版本好的引用类型,该类型将整数值与引用关联起来,可用于原子更新数据及其版本号,可以解决CAS进行原子更新出现的ABA问题。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static class Player{
public volatile int number; //变量number必须是 public属性 和 volatile
public Player(int number){ this.number = number; }
public int getNumber() { return number; }
}
static AtomicIntegerFieldUpdater<Player> updater = AtomicIntegerFieldUpdater.newUpdater(Player.class, "number");
public static void main(String[] args){
Player kawhi = new Player(1);
System.out.println(updater.getAndIncrement(kawhi)); //返回旧值1
System.out.println(updater.get(kawhi)); //返回新值2
}