我們知道synchronized 關鍵字採用的是悲觀鎖的方式實現同步,但這對併發性的影響較大。atomic包提供了一系列的操作簡單、效能高效並能保證執行緒安全的類去更新基本型別變數、陣列元素、引用型別等。atomic包下的這些類都是採用的是樂觀鎖策略去原子更新資料,使用cas操作具體實現。
1.cas操作
cas有3個運算元,記憶體值v,舊的預期值a,要修改的新值b。當且僅當預期值a和記憶體值v相同時,將記憶體值v修改為b,否則什麼都不做。因此當多個執行緒同時嘗試使用cas更新乙個變數時,任何時候只有乙個執行緒可以更新成功,若更新失敗,執行緒會重新進入迴圈再次進行嘗試。
如果對上面這個描述不能理解的話,看一下這個例子。比如執行緒a和執行緒b同時在對int型的變數 i 進行自增操作。假如 i=0,此時執行緒a和執行緒b同時啟動,它們極有可能返回的結果都是1。仔細分析一下執行過程:執行緒a啟動,並將變數 i=0 拷貝到工作記憶體a;此時執行緒b也啟動,並且將變數i=0 拷貝到工作記憶體b;當a完成操作後將 i=1 寫回主記憶體,執行緒b也做同樣的操作。此時的結果當然都是1,因為執行緒b根本就不知道執行緒a已經更新了主記憶體的值啊。這就是乙個典型的執行緒安全問題,我們當然可以用互斥鎖來實現執行緒安全,只需要讓執行緒a訪問i的時候拒絕執行緒b的訪問,但是也可以用樂觀鎖來實現,例如cas操作。當執行緒a將自己工作記憶體裡的i(此時為1)更新到主記憶體時,做一次cas操作,a預期主記憶體裡的i應該是0,而事實就是如此,因此a將新值1更新給i。接下來讓執行緒b嘗試寫回主記憶體,b預期的i也應該是0(因為b看到的i的舊值就是0),結果看到的i實際值是1,那麼執行緒b會拿走這個值1(因為b意識到了這段時間內i被更改了),進行自增操作後再次試圖更新i(此時b的工作記憶體裡i=2),這次發現主記憶體裡i確實是預期的1,於是更新主記憶體裡的i為2,這就是正確的邏輯了。
總的來說,記憶體值v指的是執行緒在工作記憶體裡更新完了之後試圖將值更新到主記憶體的時候主記憶體裡的值,舊的預期值a指執行緒從主記憶體取值時的值,新值b指的是工作記憶體裡更新完了之後試圖將值更新到主記憶體的時候工作記憶體裡的值。
由於這裡主要講cas操作主要是為了總結atomic包,對cas引發的aba問題不做說明。
2.atomic包提供原子更新基本型別的工具類
atomicboolean:以原子更新的方式更新boolean;
atomicinteger:以原子更新的方式更新integer;
atomiclong:以原子更新的方式更新long;
這幾個類的用法基本一致,這裡以atomicinteger為例總結常用的方法:
addandget(int delta) :以原子方式將輸入的數值與例項中原本的值相加,並返回最後的結果;
incrementandget() :以原子的方式將例項中的原值進行加1操作,並返回最終相加後的結果;
getandset(int newvalue):將例項中的值更新為新值,並返回舊值;
getandincrement():以原子的方式將例項中的原值加1,返回的是自增前的舊值;
還有一些方法,可以檢視api,不再贅述。想知道方法原理也可以直接查api,其實很多方法看方法名就知道執行結果了,下面用乙個簡單的例子來說明atomicinteger的用法:
public
class
atomicdemo
}輸出結果:12
4455
52
3.atomic包提供原子更新陣列的工具類
atomicintegerarray:原子更新整型陣列中的元素;
atomiclongarray:原子更新長整型陣列中的元素;
atomicreferencearray:原子更新引用型別陣列中的元素
這幾個類的用法一致,就以atomicintegerarray來總結下常用的方法:
addandget(int i, int delta):以原子更新的方式將陣列中索引為i的元素與輸入值相加;
getandincrement(int i):以原子更新的方式將陣列中索引為i的元素自增加1;
compareandset(int i, int expect, int update):將陣列中索引為i的位置的元素進行更新
可以看出,atomicintegerarray與atomicinteger的方法類似,只不過在atomicintegerarray的方法中會多乙個指定陣列索引位。下面舉乙個簡單的使用getandadd()方法的例子:
public
class
atomicdemo
;private
static atomicintegerarray integerarray =
newatomicintegerarray
(value)
;public
static
void
main
(string[
] args)
}輸出結果:
72
4.原子更新引用型別atomicreference:原子更新引用型別;
atomicreferencefieldupdater:原子更新引用型別裡的字段;
atomicmarkablereference:原子更新帶有標記位的引用型別;
這幾個類的使用方法也是基本一樣的,以atomicreference為例,來說明這些類的基本用法。下面是乙個示例:
public
class
atomicdemo
static
class
user
@override
public string tostring()
';}}
}輸出結果:
user
user
注意此處reference.getandset(user2)方法是先返回reference指向的例項,再將reference指向user2。
總的來說,atomic包提供了一些類,它們以原子操作的方式更新變數,實現原理就是cas操作,在這篇部落格裡我就不對原理加以展示了,官方文件有很詳細的說明。
atomic包中的類概述
這是乙個小工具包,他的實際作用是提供了很多個無阻塞 的執行緒安全的變數操作工具。無阻塞的執行緒安全 其含義就是不使用synchronize,而使用volatile cas的方式實現。類描述 atomicboolean 針對乙個 boolean 型別的變數做原子更新操作。atomicinteger 針...
執行緒安全性 原子性 Atomic包 1
定義 當多個執行緒訪問某個類時,不管執行時環境採用何種排程方式或者這些程序將如何交替執行,並且在主調 中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那麼就稱這個類是執行緒安全的。原子性 提供互斥訪問,在同一時刻只能有乙個執行緒對他訪問 可見性 乙個執行緒對記憶體的修改可以及時的被其他執行...
atomic 和 nonatomic的區別
原子性和非原子性的區別 兩個關鍵字修飾的oc物件,系統都會自動生成setter getter方法,區別就在於乙個會進行加鎖操作,乙個不會。系統預設是使用atomic的。因為atomic做了執行緒鎖,所以理論上講atomic是要比nonatomic更加耗費效能,更慢。理論上如果沒有特殊要求的話,ios...