摘要:所謂,就是可以以一種線程安全的方式操作非線程安全對象的某些字段。我們來對上述代碼進(jìn)行改造賬戶類改造引入通過操作字段調(diào)用方,并未做任何改變上述代碼,無論執(zhí)行多少次,最終結(jié)果都是,因為這回是線程安全的。這也是整個包的設(shè)計理念之一。
本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog...一、什么是FieldUpdater
在java.util.concurrent.atomic包中,由三個比較特殊的原子類:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater。
通過名稱可以看到,這幾類的功能大致相同,只是針對的類型有所不同。
所謂AtomicXXXFieldUpdater,就是可以以一種線程安全的方式操作非線程安全對象的某些字段。光這么說有點難理解,我們通過一個例子來看下。
假設(shè)有一個公司賬戶Account,100個人同時往里面存錢1塊錢,那么正常情況下,最終賬戶的總金額應(yīng)該是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 + "}"; } }
調(diào)用類:
public class FieldUpdaterTest { public static void main(String[] args) throws InterruptedException { Account account = new Account(0); // 初始金額0 Listlist = 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) { // 等待所有線程執(zhí)行完成 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做并發(fā)控制,最終賬戶金額很可能小于100。
按照之前學(xué)習(xí)的atomic框架,可以將Account類的int類型字段改為AtomicInteger,或者在Task任務(wù)類中,將所有涉及到共享變量的地方都加鎖訪問。
那么,還有沒有其它解決方式?
本章開頭我們講到,AtomicXXXFieldUpdater可以以一種線程安全的方式操作非線程安全對象的某些字段。
這里,Account就是非線程安全對象,money就是需要操作的字段。
我們來對上述代碼進(jìn)行改造:
賬戶類Account改造:
class Account { private volatile int money; private static final AtomicIntegerFieldUpdaterupdater = 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 + "}"; } }
調(diào)用方,并未做任何改變:
public class FieldUpdaterTest { public static void main(String[] args) throws InterruptedException { Account account = new Account(0); Listlist = 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(); } } }
上述代碼,無論執(zhí)行多少次,最終結(jié)果都是“100”,因為這回是線程安全的。
對比下改造,可以發(fā)現(xiàn),AtomicIntegerFiledUpdater的引入,使得我們可以在不修改用戶代碼(調(diào)用方)的情況下,就能實現(xiàn)并發(fā)安全性。
唯一的改變之處就是Account內(nèi)部的改造:
這也是AtomicXXXFieldUpdater引入的一個重要原因,單純從功能上來講,能用AtomicXXXFieldUpdater實現(xiàn)的并發(fā)控制,同步器和其它原子類都能實現(xiàn),但是使用AtomicXXXFieldUpdater,符合面向?qū)ο笤O(shè)計的一個基本原則——開閉原則,尤其是對一些遺留代碼的改造上。
另外,使用AtomicXXXFieldUpdater,不需要進(jìn)行任何同步處理,單純的使用CAS+自旋操作就可以實現(xiàn)同步的效果。這也是整個atomic包的設(shè)計理念之一。
二、AtomicReferenceFieldUpdater原理AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater這三個類大同小異,AtomicIntegerFieldUpdater只能處理int原始類型的字段,AtomicLongFieldUpdater只能處理long原始類型的字段,AtomicReferenceFieldUpdater可以處理所有引用類型的字段。
本節(jié)以AtomicReferenceFieldUpdater為例,介紹下FiledUpdater的基本原理。
AtomicReferenceFieldUpdater對象的創(chuàng)建AtomicReferenceFieldUpdater本身是一個抽象類,沒有公開的構(gòu)造器,只能通過靜態(tài)方法newUpdater創(chuàng)建一個AtomicReferenceFieldUpdater子類對象:
newUpdater的三個入?yún)⒑x如下:
入?yún)⒚Q | 含義 |
---|---|
tclass | 目標(biāo)對象的類型 |
vclass | 目標(biāo)字段的類型 |
fieldName | 目標(biāo)字段名 |
AtomicReferenceFieldUpdaterImpl是AtomicReferenceFieldUpdater的一個內(nèi)部類,并繼承了AtomicReferenceFieldUpdater。AtomicReferenceFieldUpdater的API,基本都是委托AtomicReferenceFieldUpdaterImpl 來實現(xiàn)的。
來看下AtomicReferenceFieldUpdaterImpl 對象的構(gòu)造,其實就是一系列的權(quán)限檢查:
通過源碼,可以看到AtomicReferenceFieldUpdater的使用必須滿足以下條件:
AtomicReferenceFieldUpdater只能修改對于它可見的字段,也就是說對于目標(biāo)類的某個字段field,如果修飾符是private,但是AtomicReferenceFieldUpdater所在的使用類不能看到field,那就會報錯;
目標(biāo)類的操作字段,必須用volatile修飾;
目標(biāo)類的操作字段,不能是static的;
AtomicReferenceFieldUpdater只適用于引用類型的字段;
AtomicReferenceFieldUpdater的方法原理AtomicReferenceFieldUpdater中所有的方法都是基于Unsafe類操作,看下最常用的方法compareAndSet:
通過偏移量offset獲取字段的地址,然后利用Unsafe進(jìn)行CAS更新。
其它方法也大同小異,讀者可以參考Oracle官方文檔和JDK源碼。
AtomicReferenceFieldUpdater接口聲明文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/76610.html
摘要:整個包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執(zhí)行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據(jù)一系列常見的多線程設(shè)計模式,設(shè)計了并發(fā)包,其中包下提供了一系列基礎(chǔ)的鎖工具,用以對等進(jìn)行補充增強。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發(fā)于一世流云專欄:https...
摘要:注意原子數(shù)組并不是說可以讓線程以原子方式一次性地操作數(shù)組中所有元素的數(shù)組。類的方法返回指定類型數(shù)組的元素所占用的字節(jié)數(shù)。,是將轉(zhuǎn)換為進(jìn)制,然后從左往右數(shù)連續(xù)的個數(shù)。 showImg(https://segmentfault.com/img/remote/1460000016012145); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... 一...
摘要:本身不直接支持指針的操作,所以這也是該類命名為的原因之一。中的許多方法,內(nèi)部其實都是類在操作。 showImg(https://segmentfault.com/img/remote/1460000016012251); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... 一、Unsafe簡介 在正式的開講 juc-atomic框架系列之前,有...
摘要:顧名思義,是類型的線程安全原子類,可以在應(yīng)用程序中以原子的方式更新值。創(chuàng)建對象先來看下對象的創(chuàng)建。也就是說當(dāng)一個線程修改一個共享變量時,其它線程能立即讀到這個修改的值。 showImg(https://segmentfault.com/img/remote/1460000016012210); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... ...
摘要:但是,有些操作會依賴于對象的變化過程,此時的解決思路一般就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那么就會變成。四的引入就是上面所說的加了版本號的。 showImg(https://segmentfault.com/img/remote/1460000016012188); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blo...
閱讀 1329·2021-09-04 16:40
閱讀 3455·2021-07-28 00:13
閱讀 2878·2019-08-30 11:19
閱讀 2611·2019-08-29 12:29
閱讀 3167·2019-08-29 12:24
閱讀 1122·2019-08-26 13:28
閱讀 2386·2019-08-26 12:01
閱讀 3445·2019-08-26 11:35