原子操做是指不會被線程調度機制打斷的操做,這種操做一旦開始,就一直運行到結束,中間不會有任何線程上下文切換。java
原子操做能夠是一個步驟,也能夠是多個操做步驟,可是其順序不能夠被打亂,也不能夠被切割而只執行其中的一部分,將整個操做視做一個總體是原子性的核心特徵。git
在java中提供了不少原子類,筆者在此主要把這些原子類分紅四大類。數據庫
若是是基本類型,則替換其值,若是是引用,則替換其引用地址,這些類主要有:數組
(1)AtomicBoolean緩存
原子更新布爾類型,內部使用int類型的value存儲1和0表示true和false,底層也是對int類型的原子操做。安全
(2)AtomicInteger架構
原子更新int類型。分佈式
(3)AtomicLongide
原子更新long類型。源碼分析
(4)AtomicReference
原子更新引用類型,經過泛型指定要操做的類。
(5)AtomicMarkableReference
原子更新引用類型,內部使用Pair承載引用對象及是否被更新過的標記,避免了ABA問題。
(6)AtomicStampedReference
原子更新引用類型,內部使用Pair承載引用對象及更新的郵戳,避免了ABA問題。
這幾個類的操做基本相似,底層都是調用Unsafe的compareAndSwapXxx()來實現,基本用法以下:
private static void testAtomicReference() {
AtomicInteger atomicInteger = new AtomicInteger(1);
atomicInteger.incrementAndGet();
atomicInteger.getAndIncrement();
atomicInteger.compareAndSet(3, 666);
System.out.println(atomicInteger.get());
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
atomicStampedReference.compareAndSet(1, 2, 1, 3);
atomicStampedReference.compareAndSet(2, 666, 3, 5);
System.out.println(atomicStampedReference.getReference());
System.out.println(atomicStampedReference.getStamp());
}
複製代碼
原子更新數組中的元素,能夠更新數組中指定索引位置的元素,這些類主要有:
(1)AtomicIntegerArray
原子更新int數組中的元素。
(2)AtomicLongArray
原子更新long數組中的元素。
(3)AtomicReferenceArray
原子更新Object數組中的元素。
這幾個類的操做基本相似,更新元素時都要指定在數組中的索引位置,基本用法以下:
private static void testAtomicReferenceArray() {
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
atomicIntegerArray.getAndIncrement(0);
atomicIntegerArray.getAndAdd(1, 666);
atomicIntegerArray.incrementAndGet(2);
atomicIntegerArray.addAndGet(3, 666);
atomicIntegerArray.compareAndSet(4, 0, 666);
System.out.println(atomicIntegerArray.get(0));
System.out.println(atomicIntegerArray.get(1));
System.out.println(atomicIntegerArray.get(2));
System.out.println(atomicIntegerArray.get(3));
System.out.println(atomicIntegerArray.get(4));
System.out.println(atomicIntegerArray.get(5));
}
複製代碼
原子更新對象中的字段,能夠更新對象中指定字段名稱的字段,這些類主要有:
(1)AtomicIntegerFieldUpdater
原子更新對象中的int類型字段。
(2)AtomicLongFieldUpdater
原子更新對象中的long類型字段。
(3)AtomicReferenceFieldUpdater
原子更新對象中的引用類型字段。
這幾個類的操做基本相似,都須要傳入要更新的字段名稱,基本用法以下:
private static void testAtomicReferenceField() {
AtomicReferenceFieldUpdater<User, String> updateName = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class,"name");
AtomicIntegerFieldUpdater<User> updateAge = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
User user = new User("tong ge", 21);
updateName.compareAndSet(user, "tong ge", "read source code");
updateAge.compareAndSet(user, 21, 25);
updateAge.incrementAndGet(user);
System.out.println(user);
}
private static class User {
volatile String name;
volatile int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "name: " + name + ", age: " + age;
}
}
複製代碼
高性能原子類,是java8中增長的原子類,它們使用分段的思想,把不一樣的線程hash到不一樣的段上去更新,最後再把這些段的值相加獲得最終的值,這些類主要有:
(1)Striped64
下面四個類的父類。
(2)LongAccumulator
long類型的聚合器,須要傳入一個long類型的二元操做,能夠用來計算各類聚合操做,包括加乘等。
(3)LongAdder
long類型的累加器,LongAccumulator的特例,只能用來計算加法,且從0開始計算。
(4)DoubleAccumulator
double類型的聚合器,須要傳入一個double類型的二元操做,能夠用來計算各類聚合操做,包括加乘等。
(5)DoubleAdder
double類型的累加器,DoubleAccumulator的特例,只能用來計算加法,且從0開始計算。
這幾個類的操做基本相似,其中DoubleAccumulator和DoubleAdder底層其實也是用long來實現的,基本用法以下:
private static void testNewAtomic() {
LongAdder longAdder = new LongAdder();
longAdder.increment();
longAdder.add(666);
System.out.println(longAdder.sum());
LongAccumulator longAccumulator = new LongAccumulator((left, right)->left + right * 2, 666);
longAccumulator.accumulate(1);
longAccumulator.accumulate(3);
longAccumulator.accumulate(-4);
System.out.println(longAccumulator.get());
}
複製代碼
關於原子類的問題,筆者整理了大概有如下這些:
(1)Unsafe是什麼?
(3)Unsafe爲何是不安全的?
(4)Unsafe的實例怎麼獲取?
(5)Unsafe的CAS操做?
(6)Unsafe的阻塞/喚醒操做?
(7)Unsafe實例化一個類?
(8)實例化類的六種方式?
(9)原子操做是什麼?
(10)原子操做與數據庫ACID中A的關係?
(11)AtomicInteger怎麼實現原子操做的?
(12)AtomicInteger主要解決了什麼問題?
(13)AtomicInteger有哪些缺點?
(14)ABA是什麼?
(15)ABA的危害?
(16)ABA的解決方法?
(17)AtomicStampedReference是怎麼解決ABA的?
(18)實際工做中遇到過ABA問題嗎?
(19)CPU的緩存架構是怎樣的?
(20)CPU的緩存行是什麼?
(21)內存屏障又是什麼?
(22)僞共享是什麼緣由致使的?
(23)怎麼避免僞共享?
(24)消除僞共享在java中的應用?
(25)LongAdder的實現方式?
(26)LongAdder是怎麼消除僞共享的?
(27)LongAdder與AtomicLong的性能對比?
(28)LongAdder中的cells數組是無限擴容的嗎?
關於原子類的問題差很少就這麼多,都能回答上來嗎?點擊下面的連接能夠直接到相應的章節查看:
死磕 java原子類之AtomicStampedReference源碼分析
原子類系列源碼分析到此就結束了,雖然分析的類比較少,可是牽涉的內容很是多,特別是操做系統底層的知識,好比CPU指令、CPU緩存架構、內存屏障等。
下一章,咱們將進入「同步系列」,同步最多見的就是各類鎖了,這裏會着重分析java中的各類鎖、各類同步器以及分佈式鎖相關的內容。
歡迎關注個人公衆號「彤哥讀源碼」,查看更多源碼系列文章, 與彤哥一塊兒暢遊源碼的海洋。