摘要:一無鎖方案并發(fā)包中的原子類都是基于無鎖方案實現(xiàn)的,相較于傳統(tǒng)的互斥鎖,無鎖并沒有加鎖解鎖線程切換的消耗,因此無鎖解決方案的性能更好,同時無鎖還能夠保證線程安全。線程首先讀取的值并加,如果此時有另一個線程更新了,則期望值和不相等,更新失敗。
一、無鎖方案
Java 并發(fā)包中的原子類都是基于無鎖方案實現(xiàn)的,相較于傳統(tǒng)的互斥鎖,無鎖并沒有加鎖、解鎖、線程切換的消耗,因此無鎖解決方案的性能更好,同時無鎖還能夠保證線程安全。
1. 無鎖方案的實現(xiàn)原理無鎖主要依賴 CAS(Compare And Swap) ,即比較并交換,CAS 是一條 CPU 指令,其本身是能夠保證原子性的。CAS 中有三個參數(shù):
共享變量的內(nèi)存地址 A
用于比較的值 B
共享變量的新值 C
public class SimpleCAS { private int value; public synchronized int cas(int expectVal, int newVal){ int curVal = value; if (expectVal == curVal){ value = newVal; } return curVal; } }
上面的代碼展示了 CAS 的簡單實現(xiàn),從內(nèi)存中讀出當前 value 的值,并且需要判斷,期望值 expectVal == curVal 的時候,才會將 value 更新為新值。
仍然以上面的代碼,來實現(xiàn)一個簡單的,基于 CAS 的線程安全的 value+1 方法。這里的 cas 方法僅用于幫助理解,所以執(zhí)行結(jié)果可能有出入。
public class SimpleCAS { private volatile int value; public void addValue(){ int newVal = value + 1; while (value != cas(value, newVal)){ newVal = value + 1; } } private synchronized int cas(int expectVal, int newVal){ int curVal = value; if (expectVal == curVal){ value = newVal; } return curVal; } }
線程首先讀取 value 的值并加 1,如果此時有另一個線程更新了 value,則期望值和 value 不相等,更新失敗。更新失敗后,循環(huán)嘗試,重新讀取 value 的值,直到更新成功退出循環(huán)。
2. ABA 問題無鎖的實現(xiàn)方案中需要注意的一個問題便是 ABA 問題。
例如上面的代碼,value 的初始值為 0,線程 t1 取到了 value 的值,并將其更新為 1,然后線程又將 value 更新為 0。
假如這個過程中有另外一個線程 t2,和 t1 同時取初始值為 0 的 value,t2 在 t1 執(zhí)行完后更新 value,這個時候 value 雖然還是為 0,但已經(jīng)被 t1 修改過了。
大多數(shù)情況下,我們并不需要關(guān)心 ABA 問題,例如數(shù)值型數(shù)據(jù)的加減,但是對象類型的數(shù)據(jù)遇到了 ABA 問題的話,可能前后的屬性已經(jīng)發(fā)生了變化,所以需要解決。
解決的辦法也很簡單,給對象類型的數(shù)據(jù)加上一個版本號即可,每更新一次,版本號加 1,這樣即使對象數(shù)據(jù)從 A 變成 B 后 又變成 A,但是版本號是遞增的,就可以分辨出對象還是被修改過的。
二、原子類 1. 原子化基本數(shù)據(jù)類型有三個實現(xiàn)類:AtomicBoolean、AtomicInteger、AtomicLong
常用的方法如下,以 AtomicInteger 為例,其他的類似:
AtomicInteger i = new AtomicInteger(0); i.getAndSet(int newValue);//獲取當前值并設置新值 i.getAndIncrement();//相當于 i ++ i.incrementAndGet();//相當于 ++ i i.getAndDecrement();//相當于 i -- i.decrementAndGet();//相當于 -- i i.addAndGet(int delta);//相當于 i + delta,并返回添加后的值 i.getAndAdd(int delta);//相當于 i + delta,并返回添加前的值 i.compareAndSet(int expect, int update);//CAS 操作,返回 boolean值,表示是否更新成功 i.getAndUpdate(update -> 10);//通過函數(shù)更新值 i.updateAndGet(update -> 10);//類似上面2. 原子化對象引用類型
實現(xiàn)類分別是:AtomicReference、AtomicStampedReference、AtomicMarkableReference,其中后兩個可以實現(xiàn)了解決 ABA 問題的方案。
AtomicReference 常用的方法如下:
//假設有一個叫做 Order 的類 AtomicReferenceorderReference = new AtomicReference<>(); orderReference.getAndSet(Order newValue);//獲取并設置 orderReference.set(Order order);//設置值 Order order1 = orderReference.get();//獲取對象 orderReference.compareAndSet(Order expect, Order update);//比較交換 orderReference.getAndUpdate();//通過函數(shù)更新值 orderReference.updateAndGet();
AtomicStampedReference 需要傳入初始值和初始 stamp,其中 stamp 相當于對象的版本號(用來解決 ABA 問題),使用示例如下:
AtomicStampedReferencereference = new AtomicStampedReference<>("roseduan", 0); //嘗試修改stamp的值 boolean b = reference.attemptStamp(reference.getReference(), 10); //獲取值 String str = reference.getReference(); //獲取stamp int stamp = reference.getStamp(); //重新設置值和stamp reference.set("I am not roseduan", 20); //比較交換 boolean b1 = reference.compareAndSet("roseduan", "jack", 20, 0);
AtomicMarkableReference 使用一個 mark 標記(boolean 類型) 代替了 AtomicStampedReference 中的 stamp,用這種更簡單的方式來解決 ABA 問題。使用的方式和上面的類似,只是將方法中的 stamp 變?yōu)?boolean 類型的值即可。
3. 原子化數(shù)組類型實現(xiàn)類有三個:
AtomicIntegerArray:原子化的整型數(shù)組
AtomicLongArray:原子化長整型數(shù)組
AtomicReferenceArray:原子化對象引用數(shù)組
使用和原子化基本類型都是差不多的,只是需要在方法中加上數(shù)組下標即可。
4. 原子化對象屬性更新器也有三個實現(xiàn)類:
AtomicIntegerFieldUpdater:更新對象的整型屬性
AtomicLongFieldUpdater:更新對象的長整型屬性
AtomicReferenceFieldUpdater:更新對象的引用型屬性
這三個類都是利用 Java 的反射機制實現(xiàn)的,并且為了保證原子性,要求被更新的對象的屬性必須是 volatile 類型的。使用示例如下:
@Data @Builder public class User { private volatile int age; private volatile long number; private volatile String name; public static void main(String[] args) { User user = User.builder().age(22).number(15553663L).name("roseduan").build(); //更新age屬性的值 AtomicIntegerFieldUpdaterintegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); integerFieldUpdater.set(user, 25); //更新number屬性的值 AtomicLongFieldUpdater longFieldUpdater = AtomicLongFieldUpdater.newUpdater(User.class, "number"); longFieldUpdater.set(user, 1000101L); //更新對象類型的屬性的值 AtomicReferenceFieldUpdater referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name"); referenceFieldUpdater.set(user, "I am not roseduan"); System.out.println(user.toString()); } }
程序中創(chuàng)建了一個 User 類,有三個屬性 age、number、name 分別對應整型、長整型、引用類型。然后使用對象屬性更新器進行屬性值的更新,更新器的其他方法的使用和前面說到的幾種原子化類型類似。
5. 原子化累加器實現(xiàn)類有四個:
DoubleAdder
DoubleAccumulator
LongAdder
LongAccumulator
這幾個類的功能有限,僅用來執(zhí)行累加操作,但是速度非常快。下面介紹 DoubleAdder 和 DoubleAccumulator 的用法,其余兩個類似。
//DoubleAccumulator使用示例 DoubleAccumulator a = new DoubleAccumulator(Double::sum, 0);//設初始值為0 //累加 a.accumulate(1); a.accumulate(2); a.accumulate(3); a.accumulate(4); System.out.println(a.get());//輸出10 //DoubleAdder使用示例 DoubleAdder adder = new DoubleAdder(); adder.add(1); adder.add(2); adder.add(3); adder.add(4); adder.add(5); System.out.println(adder.intValue());//輸出15
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/74612.html
摘要:,,面向切面編程。,切點,切面匹配連接點的點,一般與切點表達式相關(guān),就是切面如何切點。例子中,注解就是切點表達式,匹配對應的連接點,通知,指在切面的某個特定的連接點上執(zhí)行的動作。,織入,將作用在的過程。因為源碼都是英文寫的。 之前《零基礎帶你看Spring源碼——IOC控制反轉(zhuǎn)》詳細講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動態(tài)代理嗎?看這篇就夠了》介紹了下...
摘要:相反,它由單體中的適配器和使用一個或多個進程間通信機制的服務組成。因為微服務架構(gòu)的本質(zhì)是一組圍繞業(yè)務功能組織的松耦合服務。如果你嘗試將此類功能實現(xiàn)為服務,則通常會發(fā)現(xiàn),由于過多的進程間通信而導致性能下降。這是快速展示微服務架構(gòu)價值的好方法。你很有可能正在處理大型復雜的單體應用程序,每天開發(fā)和部署應用程序的經(jīng)歷都很緩慢而且很痛苦。微服務看起來非常適合你的應用程序,但它也更像是一項遙不可及的必殺...
摘要:英特爾機架規(guī)模設計則能實現(xiàn)以機架為單位的軟硬件解耦,為裸金屬即服務提供容量更大的資源池,并可通過開放的和協(xié)議如和,高效發(fā)掘管理和調(diào)配這些資源。 江湖上,一直流傳著得IaaS(基礎設施即服務),得公有云天下的說法。想握緊IaaS這柄云端殺手锏,?大熱的裸金屬即服務和容器即服務,還不了解一下??它們?yōu)槭裁慈绱耸苋?..
摘要:前言本文描述線程線程狀態(tài)及狀態(tài)轉(zhuǎn)換,不會涉及過多理論,主要以代碼示例說明線程狀態(tài)如何轉(zhuǎn)換。被終止線程執(zhí)行完畢正常結(jié)束或執(zhí)行過程中因未捕獲異常意外終止都會是線程進入被終止狀態(tài)。線程執(zhí)行完畢打印狀態(tài)。 前言 本文描述Java線程線程狀態(tài)及狀態(tài)轉(zhuǎn)換,不會涉及過多理論,主要以代碼示例說明線程狀態(tài)如何轉(zhuǎn)換。 基礎知識 1. 線程狀態(tài) 線程可以有6種狀態(tài): New(新建) Runnable(可運...
摘要:鏈路追蹤鏈路追蹤一詞是在年提出的,當時谷歌發(fā)布了一篇論文,介紹了谷歌自研的分布式鏈路追蹤的實現(xiàn)原理,還介紹了他們是怎么低成本實現(xiàn)對應用透明的。感興趣的同學可以去深入了解一下鏈路追蹤,希望本文對你有所幫助。 showImg(https://upload-images.jianshu.io/upload_images/13711841-f54b415cc8d07fdc?imageMogr2...
閱讀 2895·2021-11-17 09:33
閱讀 2292·2021-09-03 10:40
閱讀 601·2019-08-29 18:45
閱讀 3020·2019-08-29 16:21
閱讀 666·2019-08-29 11:11
閱讀 3450·2019-08-26 12:00
閱讀 3021·2019-08-23 18:19
閱讀 1152·2019-08-23 12:18