第2章 對象及變量的并發(fā)訪問
概念
“非線程安全”其實(shí)會(huì)在多個(gè)線程對同一個(gè)對象中的實(shí)例變量進(jìn)行并發(fā)訪問時(shí)發(fā)生晋控,產(chǎn)生的后果就是“臟讀”簇爆,也就是取到的數(shù)據(jù)其實(shí)是被更改過的
“非線程安全”問題存在于“實(shí)例變量”中(和數(shù)據(jù)庫)侦铜,如果是方法內(nèi)部的私有變量,則不存在“非線程安全”問題,所得結(jié)果也就是“線程安全”的了君纫。
如果多個(gè)線程共同訪問1個(gè)對象中的實(shí)例變量,則有可能出現(xiàn)“非線程安全”問題芹彬。
只有共享資源(庫存)的讀寫訪問才需要同步化蓄髓。
例如
1)A線程先持有object對象的Lock鎖,B線程可以以異步的方式調(diào)用object對象中的非synchronized類型的方法舒帮。
2)A線程先持有object對象的Lock鎖会喝,B線程如果在這時(shí)調(diào)用object對象中的synchronized類型的方法則需等待,也就是同步会前。
可重入鎖:自己可以再次獲得自己的內(nèi)部鎖好乐。比如有1條線程獲得了某個(gè)對象的鎖匾竿,此時(shí)這個(gè)對象鎖還沒有釋放瓦宜,當(dāng)其于次想要獲取這個(gè)對象的鎖的時(shí)候還是可以獲取的,如果不可鎖重入的話岭妖,就會(huì)造成死鎖临庇。
可重入鎖也支持在父子類繼承的環(huán)境中。
當(dāng)存在父子類繼承關(guān)系時(shí)昵慌,子類是完全可以通過“可重入鎖”調(diào)用父類的同步方法的假夺。
當(dāng)一個(gè)線程執(zhí)行的代碼出現(xiàn)異常時(shí),其所持有的鎖會(huì)自動(dòng)釋放斋攀。
synchronized關(guān)鍵字
關(guān)鍵字
synchronized
取得的鎖都是對象鎖已卷,而不是把一段代碼或方法(函數(shù))當(dāng)作鎖,所以在上面的示例中淳蔼,哪個(gè)線程先執(zhí)行帶synchronized
關(guān)鍵字的方法侧蘸,哪個(gè)線程就持有該方法所屬對象的鎖Lock,那么其他線程只能呈等待狀態(tài),前提是多個(gè)線程訪問的是同一個(gè)對象鹉梨。同步是可以被繼承的讳癌,即父類中帶有
synchronized
關(guān)鍵字的方法在子類中也具有synchronized
關(guān)鍵字該有的特性。當(dāng)子類重寫父類這個(gè)synchronized
方法時(shí)可以不加synchronized
關(guān)鍵字存皂,這時(shí)子類重寫的這個(gè)方法也沒有同步特性了晌坤,反之亦然。
關(guān)于上一條有必要說明一下:原文中是這樣寫的“同步不能繼承旦袋,所以還得在子類的方法中添加
synchronized
關(guān)鍵字”骤菠,個(gè)人對繼承的理解是子類繼承父類后,子類繼承了父類中可以被繼承的屬性和方法疤孕,這時(shí)子類可以使用繼承過來的這些屬性與方法娩怎,并非此書作中所講到的例子用重寫來說明子類不能繼承父類帶有synchronized
關(guān)鍵字的方法的同步特性。
不在
synchronized
塊中就是異步執(zhí)行胰柑,在synchronized
塊中就是同步執(zhí)行截亦。在使用同步
synchronized(this)
代碼塊時(shí)需要注意的是爬泥,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)
同步代碼塊時(shí),其他線程對同一個(gè)object中所有其他synchronized(this)
同步代碼的訪問將被阻塞崩瓤,這說明synchronized
使用的“對象監(jiān)視器”是一個(gè)袍啡。synchronized(this)
代碼塊是鎖定當(dāng)前對象的synchronized
關(guān)鍵字加到static
靜態(tài)方法上是給Class
類上鎖,而synchronized
關(guān)鍵字加到非static
方法上是給對象上鎖却桶。多線程的死鎖:多個(gè)線程多個(gè)鎖境输,代碼問題上會(huì)出現(xiàn)死鎖∮毕担可以使用JDK自帶的工具來監(jiān)測是否有死鎖現(xiàn)象嗅剖。
1)在jdk安裝目錄下執(zhí)行命令jps
2)得到運(yùn)行的線程Run的id值1234,再執(zhí)行命令jstack嘁扼,查看結(jié)果
3)若有死鎖會(huì)有提示類似'Found 1 deadlock.'
作為鎖的對象的引用不變信粮,即使其中的屬性改變后也和之前是同一個(gè)鎖
關(guān)鍵字
synchronized
可以使多個(gè)線程訪問同一個(gè)資源具有同步性,而且它還具有將線程工作內(nèi)存中的私有變量與公共內(nèi)存中的變量同步的功能趁啸。關(guān)鍵字
synchronized
可以保證在同一時(shí)刻强缘,只有一個(gè)線程可以執(zhí)行某一個(gè)方法或某一個(gè)代碼塊。它包含兩個(gè)特征:互斥性和可見性不傅。同步synchronized
不僅可以解決一個(gè)線程看到對象處于不一致的狀態(tài)旅掂,還可以保證進(jìn)入同步方法或者同步代碼塊的每個(gè)線程,都看到由同一個(gè)鎖保護(hù)之前所有的修改效果访娶。(辯證著看)
volatile關(guān)鍵字
- volatile的主要作用是使變量在多個(gè)線程間可見商虐,但其和原子性并沒有什么關(guān)系。(但在這個(gè)服務(wù)器集群的年代單實(shí)例上的共享變量可見性可能并沒什么大用)
volatile private boolean isRunning = false;
volatile修飾的屬性當(dāng)被修改時(shí)會(huì)強(qiáng)制將修改的值立即寫入主存崖疤,此時(shí)會(huì)使其它線程工作內(nèi)存中的此變量緩存失效秘车,當(dāng)這些線程要讀取時(shí)發(fā)現(xiàn)緩存失效就會(huì)去主存中讀最新的值。
synchronized
與volatile
的比較
1)volatile是線程同步的輕量實(shí)現(xiàn)戳晌,所以性能肯定比synchronized要好鲫尊。volatile只能修飾變量,synchronized可以修飾方法和代碼塊沦偎。
2)多線程訪問volatile不會(huì)發(fā)生阻塞疫向,而synchronized會(huì)。
3)volatile能保證數(shù)據(jù)的可見性豪嚎,但不能保證原子性搔驼;而synchronized可以保證原子性也可以間接保證可見性,因?yàn)樗鼤?huì)將私有內(nèi)存和公共內(nèi)存中的數(shù)據(jù)做同步侈询。(個(gè)人認(rèn)為并非如此而是happen-before的關(guān)系)
4)volatile解決的是變量在多線程之間的可見性舌涨;而synchronized解決的是多個(gè)線程之間訪問資源的同步性。
volatile關(guān)鍵字解決的是變量讀時(shí)的可見性問題扔字,但無法保證原子性囊嘉,對于多線程訪問同一個(gè)實(shí)例變量還是需要加鎖同步温技。
線程安全包括原子性和可見性兩個(gè)方面,Java的同步機(jī)制都是圍繞這兩個(gè)方面來確保線程安全的扭粱。
原子操作是不能分割的整體舵鳞,沒有其他線程能夠中斷或檢查正在原子操作中的變量