概述:
1、原子操作對線程安全并無任何安全保證。被atomic修飾的屬性(不重載設(shè)置器和訪問器)只保證了對數(shù)據(jù)讀寫的完整性赠潦,也就是原子性是辕,但是與對象的線程安全無關(guān)薄辅。
2静浴、線程安全已經(jīng)有保障情況下勿她、對性能也有要求的情況下可使用nonatomic替代atomic即横,當(dāng)然也可以一直使用atomic噪生。
描述:
首先我要糾正一個網(wǎng)上常見的關(guān)于atomic非線程安全的舉例:如果線程 A 調(diào)了 getter,與此同時線程 B 东囚、線程 C 都調(diào)了 setter——那最后線程 A get 到的值跺嗽,有3種可能:可能是 B、C set 之前原始的值页藻,也可能是 B set 的值桨嫁,也可能是 C set 的值。同時份帐,最終這個屬性的值璃吧,可能是 B set 的值,也有可能是 C set 的值废境。所以atomic可并不能保證對象的線程安全畜挨。
類似的這個例子相信很多人都見過,看起來也非常合理噩凹,沒什么錯朦促;但細(xì)琢磨,這個例子本身沒問題栓始,但根本不能證明atomic的非線程安全這個觀點(diǎn)!所以面試的時候如果舉這個例子Q薄(說明你就沒明白atomic的非線程安全性)
首先你得知道什么是線程不安全幻赚,線程的不安全是由于多線程訪問和修改共享資源而引起的不可預(yù)測的結(jié)果(有可能crash)禀忆。可以簡單理解為我們拿到的值是錯的落恼。這個例子中箩退,如果線程A getter到的值是個錯誤的值才能說是線程不安全的,可是這個例子就算線程A可能取到好幾種值佳谦,你能說取值不對嗎戴涝;不能。所以這個例子是個錯誤的例子钻蔑!
atomic的原子性和nonatomic的非原子性
atomic :系統(tǒng)自動生成的getter/setter方法會進(jìn)行加鎖操作啥刻;可以理解過讀寫鎖,可以保證讀寫安全咪笑;較耗時可帽;
nonatomic : 系統(tǒng)自動生成的getter/setter方法不會進(jìn)行加鎖操作;但速度會更快窗怒;
下面是兩個nonatomic和atomic修飾的變量映跟,我們用代碼掩飾其內(nèi)部實(shí)現(xiàn);
atomic的原子性和nonatomic的非原子性官方解釋
原子的(默認(rèn))
--原子是默認(rèn)設(shè)置:如果您不輸入任何內(nèi)容扬虚,則您的屬性是原子的努隙。
--原子屬性可以保證,如果您嘗試從中讀取內(nèi)容辜昵,則將取回有效值荸镊。
--它不能保證該值是多少,但是您將獲得合法有效的數(shù)據(jù)路鹰,而不僅僅是垃圾內(nèi)存(又叫臟數(shù)據(jù))贷洲。
--這允許您執(zhí)行的操作是,如果您有多個線程或多個進(jìn)程指向一個變量晋柱,則一個線程可以讀取而另一個線程可以寫入优构。
--如果它們同時命中,則保證讀取器線程獲得兩個值之一:更改之前或更改之后雁竞。
--原子不會給您帶來任何保證钦椭,您可能會獲得其中哪些值。
--原子通常確實(shí)與線程安全混淆碑诉,這是不正確的彪腔。
--您需要以其他方式保證線程安全。但是进栽,atomic可以保證德挣,如果您嘗試讀取數(shù)據(jù),則肯定會獲得某種合法數(shù)據(jù)快毛。
非原子
--另一方面格嗅,您可能會猜到番挺,非原子意味著“不要做原子的事情”。
--您所失去的是保證您總是能得到一些回報屯掖。
--如果嘗試在寫入過程中進(jìn)行讀取玄柏,則可能會獲取垃圾數(shù)據(jù)。
--但是贴铜,另一方面粪摘,您走得更快。
--因?yàn)樵訉傩员仨氉鲆恍┠g(shù)(遞歸鎖)才能保證您將獲得一個值绍坝,所以它們要慢一些徘意。
--如果這是您經(jīng)常訪問的屬性,則可能需要降低為非原子屬性陷嘴,以確保不會造成速度損失映砖。
分析atomic為什么不是線程安全
其實(shí)現(xiàn)在一想很奇怪,為什么要把a(bǔ)tomic和線程安全聯(lián)系在一起去探究灾挨;atomic只是對屬性的getter/setter方法進(jìn)行了加鎖操作邑退,這種安全僅僅是get/set的讀寫安全,僅此之一劳澄,但是線程安全還有除了讀寫的其他操作,比如:當(dāng)一個線程正在get/set時地技,另一個線程同時進(jìn)行release操作,可能會直接crash秒拔。很明顯atomic的讀寫鎖不能保證線程安全莫矗。 下面兩個例子寫的就挺好,挺簡單:
eg1:如果定義屬性NSInteger i是原子的砂缩,對i進(jìn)行i = i + 1操作就是不安全的作谚; 因?yàn)樵有灾荒鼙WC讀寫安全,而該表達(dá)式需要三步操作:
1庵芭、讀取i的值存入寄存器妹懒;
2、將i加1双吆;
3眨唬、修改i的值;
如果在第一步完成的時候好乐,i被其他線程修改了匾竿,那么表達(dá)式執(zhí)行的結(jié)果就與預(yù)期的不一樣,也就是不安全的
?eg2:
結(jié)果可能是[10000,20000]之間的某個值蔚万,而我們想要的結(jié)果是20000岭妖;很明顯這個例子就會引起線程隱患,而atomic并不能防止這個問題;所以我們說atomic不是線程安全昵慌;
擴(kuò)展:探索nonatomic非線程安全的原因
為什么nonatomic是非線程安全的苔巨,我們來看看runtime的源碼:
根據(jù)源碼,我們可以看到废离,getter是不會對屬性進(jìn)行retain的,假設(shè)當(dāng)getter執(zhí)行后礁芦,切換到另一個線程蜻韭,執(zhí)行setter,setter會對oldValue release柿扣,導(dǎo)致oldValue釋放肖方。再切回執(zhí)行g(shù)etter的線程,getter用到的是已經(jīng)釋放的oldValue未状。就會發(fā)生EXC_BAD_ACCESS的crash俯画。
一般情況下,getter執(zhí)行后司草,會在外部對getter獲取的屬性進(jìn)行retain艰垂,也就是調(diào)用objc_retain。但是也許就在getter發(fā)生之后埋虹,objc_retain之前其他線程執(zhí)行了setter猜憎。這時候,就會導(dǎo)致objc_retain產(chǎn)生EXC_BAD_ACCESS搔课。
那么atomic會不會發(fā)生問題呢胰柑?根據(jù)源碼,在獲取到屬性時爬泥,atomic下getter會立即對value進(jìn)行retain柬讨,即使setter對oldValue release了。由于getter已經(jīng)進(jìn)行retain袍啡,屬性不會立即釋放踩官。只有使用完成之后才會釋放。所以atomic在操作屬性的時候可以保證不會crash葬馋。
再次來個總結(jié):
nonatomic在線程間切換操作屬性的時候容易造成crash卖鲤,是不安全的。
atomic在線程間切換操作屬性可以保證不會crash畴嘶,也會get到合法的有效數(shù)據(jù)蛋逾,但是并不能保證獲取的數(shù)據(jù)就是你的預(yù)期值。也是不安全的窗悯。
借鑒文章: