本文首發於一世流雲的專欄: https://segmentfault.com/blog...
在java.util.concurrent.atomic
包中,由三個比較特殊的原子類:AtomicIntegerFieldUpdater
、AtomicLongFieldUpdater
、AtomicReferenceFieldUpdater
。
經過名稱能夠看到,這幾類的功能大體相同,只是針對的類型有所不一樣。java
所謂AtomicXXXFieldUpdater,就是能夠以一種線程安全的方式操做非線程安全對象的某些字段。光這麼說有點難理解,咱們經過一個例子來看下。segmentfault
假設有一個公司帳戶Account,100我的同時往裏面存錢1塊錢,那麼正常狀況下,最終帳戶的總金額應該是100。
先來看下線程不安全的方式:安全
帳戶類:併發
class Account { private volatile int money; Account(int initial) { this.money = initial; } public void increMoney() { money++; } public int getMoney() { return money; } @Override public String toString() { return "Account{" + "money=" + money + '}'; } }
調用類:框架
public class FieldUpdaterTest { public static void main(String[] args) throws InterruptedException { Account account = new Account(0); // 初始金額0 List<Thread> list = new ArrayList<>(); for (int i = 0; i < 100; i++) { Thread t = new Thread(new Task(account)); list.add(t); t.start(); } for (Thread t : list) { // 等待全部線程執行完成 t.join(); } System.out.println(account.toString()); } private static class Task implements Runnable { private Account account; Task(Account account) { this.account = account; } @Override public void run() { account.increMoney(); // 增長帳戶金額 } } }
上述未對Account作併發控制,最終帳戶金額極可能小於100。
按照以前學習的atomic框架,能夠將Account類的int類型字段改成AtomicInteger,或者在Task任務類中,將全部涉及到共享變量的地方都加鎖訪問。ide
那麼,還有沒有其它解決方式?學習
本章開頭咱們講到,AtomicXXXFieldUpdater能夠以一種線程安全的方式操做非線程安全對象的某些字段。
這裏,Account就是非線程安全對象,money就是須要操做的字段。this
咱們來對上述代碼進行改造:
帳戶類Account改造:atom
class Account { private volatile int money; private static final AtomicIntegerFieldUpdater<Account> updater = AtomicIntegerFieldUpdater.newUpdater(Account.class, "money"); // 引入AtomicIntegerFieldUpdater Account(int initial) { this.money = initial; } public void increMoney() { updater.incrementAndGet(this); // 經過AtomicIntegerFieldUpdater操做字段 } public int getMoney() { return money; } @Override public String toString() { return "Account{" + "money=" + money + '}'; } }
調用方,並未作任何改變:spa
public class FieldUpdaterTest { public static void main(String[] args) throws InterruptedException { Account account = new Account(0); List<Thread> list = new ArrayList<>(); for (int i = 0; i < 100; i++) { Thread t = new Thread(new Task(account)); list.add(t); t.start(); } for (Thread t : list) { t.join(); } System.out.println(account.toString()); } private static class Task implements Runnable { private Account account; Task(Account account) { this.account = account; } @Override public void run() { account.increMoney(); } } }
上述代碼,不管執行多少次,最終結果都是「100」,由於這回是線程安全的。
對比下改造,能夠發現,AtomicIntegerFiledUpdater的引入,使得咱們能夠在不修改用戶代碼(調用方)的狀況下,就能實現併發安全性。
惟一的改變之處就是Account內部的改造:
這也是AtomicXXXFieldUpdater引入的一個重要緣由,單純從功能上來說,能用AtomicXXXFieldUpdater實現的併發控制,同步器和其它原子類都能實現,可是使用AtomicXXXFieldUpdater,符合面向對象設計的一個基本原則——開閉原則,尤爲是對一些遺留代碼的改造上。
另外,使用AtomicXXXFieldUpdater,不須要進行任何同步處理,單純的使用CAS+自旋操做就能夠實現同步的效果。這也是整個atomic包的設計理念之一。
AtomicIntegerFieldUpdater
、AtomicLongFieldUpdater
、AtomicReferenceFieldUpdater
這三個類大同小異,AtomicIntegerFieldUpdater只能處理int原始類型的字段,AtomicLongFieldUpdater只能處理long原始類型的字段,AtomicReferenceFieldUpdater能夠處理全部引用類型的字段。
本節以AtomicReferenceFieldUpdater爲例,介紹下FiledUpdater的基本原理。
AtomicReferenceFieldUpdater自己是一個抽象類,沒有公開的構造器,只能經過靜態方法newUpdater建立一個AtomicReferenceFieldUpdater子類對象:
newUpdater的三個入參含義以下:
入參名稱 | 含義 |
---|---|
tclass | 目標對象的類型 |
vclass | 目標字段的類型 |
fieldName | 目標字段名 |
AtomicReferenceFieldUpdaterImpl是AtomicReferenceFieldUpdater的一個內部類,並繼承了AtomicReferenceFieldUpdater。AtomicReferenceFieldUpdater的API,基本都是委託AtomicReferenceFieldUpdaterImpl 來實現的。
來看下AtomicReferenceFieldUpdaterImpl 對象的構造,其實就是一系列的權限檢查:
經過源碼,能夠看到AtomicReferenceFieldUpdater的使用必須知足如下條件:
AtomicReferenceFieldUpdater中全部的方法都是基於Unsafe類操做,看下最經常使用的方法compareAndSet:
經過偏移量offset獲取字段的地址,而後利用Unsafe進行CAS更新。
其它方法也大同小異,讀者能夠參考Oracle官方文檔和JDK源碼。