1.8新增的原子類
DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64
1 原子更新基本類型
發展至JDk1.8,基本類型原子類有以下幾個:
AtomicBoolean、AtomicInteger、AtomicLong、DoubleAccumulator、DoubleAdder、
LongAccumulator、LongAdder
大致可以歸爲3類
AtomicBoolean、AtomicInteger、AtomicLong 元老級的原子更新,方法幾乎一模一樣
DoubleAdder、LongAdder 對Double、Long的原子更新性能進行優化提升
DoubleAccumulator、LongAccumulator 支持自定義運算
2 原子更新數組類型
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
3 原子地更新屬性
原子地更新某個類裏的某個字段時,就需要使用原子更新字段類,Atomic包提供了以下4個類進行原子字段更新
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference、AtomicReferenceFieldUpdater
使用上述類的時候,必須遵循以下原則
字段必須是volatile類型的,在線程之間共享變量時保證立即可見
字段的描述類型是與調用者與操作對象字段的關係一致。
也就是說調用者能夠直接操作對象字段,那麼就可以反射進行原子操作。
對於父類的字段,子類是不能直接操作的,儘管子類可以訪問父類的字段。
只能是實例變量,不能是類變量,也就是說不能加static關鍵字。
只能是可修改變量,不能使final變量,因爲final的語義就是不可修改。
對於AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long類型的字段,不能修改其包裝類型 (Integer/Long)。
如果要修改包裝類型就需要使用AtomicReferenceFieldUpdater。
4 原子更新引用
AtomicReference:用於對引用的原子更新
AtomicMarkableReference:帶版本戳的原子引用類型,版本戳爲boolean類型。
AtomicStampedReference:帶版本戳的原子引用類型,版本戳爲int類型。
Demo:
/**
* - AtomicInteger Demo
* @author
* @date
*/
public class Demo1 {
private static AtomicInteger sum = new AtomicInteger(0);
public static void inCreate() {
sum.incrementAndGet();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
inCreate();
System.out.println(sum);
}
}).start();
}
}
}
/**
* - 輸入一個數字,如果比上一個大,則返回當前,反之,則返回上一個.
* @param args
* @author:
* @date:
*/
public static void main(String[] args) {
// 第一個參數是一個 運算或者比較函數, 第二個是 第一個函數的初始值.
//LongAccumulator la = new LongAccumulator((left, right) -> left > right ? left : right, 4L);
LongAccumulator la = new LongAccumulator((left, right) -> left * right, 4L);
// 傳入 參數進去 與 初始值 運算 或者比較.
la.accumulate(5L);
System.out.println(la.get());
la.accumulate(4L);
System.out.println(la.get());
la.accumulate(3L);
System.out.println(la.get());
}
}
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
int[] arr = new int[] {2,4,0};
AtomicIntegerArray aia = new AtomicIntegerArray(arr);
aia.set(2, aia.addAndGet(0, 8));
/*for (int i = 0; i < aia.length(); i++) {
System.out.println(aia.get(i)); //aia [10,4,10] 原數組 arr 不受影響.
}*/
aia.accumulateAndGet(1, 5, (x, y) -> x * y);
for (int i = 0; i < aia.length(); i++) {
System.out.println(aia.get(i)); //aia [10,20,10]
}
}
}
public class AtomicLongFieldUpdaterDemo {
public static void main(String[] args) {
AtomicLongFieldUpdater<Student> au = AtomicLongFieldUpdater.newUpdater(Student.class, "id");
Student student = new Student(3L, "zhansan");
au.compareAndSet(student, 3L, 100L);
au.accumulateAndGet(student, 35L, (x, y) -> x + y);
AtomicReferenceFieldUpdater<Student, String> arfu = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
arfu.set(student, "lisi");
// arfu.compareAndSet(student, "zhansan", "wangwu"); // 這句會更新失敗. name 已經不是"張三"
arfu.compareAndSet(student, "lisi", "wangwu");
System.out.println(student.toString());
}
}
/*
* 注意:
* 1. 要修改的字段必須是 volatile 類型.
* 2. Integer 和 Long 只能 修改 int 和 long 基本類型,而不能修改包裝類型. volatile long id; volatile Long id;
* 如果要修改包裝類型,用 AtomicReferenceFieldUpdater
* 3. 不能操作 父類字段.
*/
class Student {
volatile long id;
volatile String name;
public Student(Long id, String name) {
super();
this.id = id;
this.name = name;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
public final Long getId() {
return id;
}
public final void setId(Long id) {
this.id = id;
}
public final String getName() {
return name;
}
public final void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
---end.