問:簡(jiǎn)單談?wù)勀銓?duì) Java synchronized 關(guān)鍵字的理解?
答:
synchronized 關(guān)鍵字主要用來解決多線程并發(fā)同步問題脓斩,可以用來修飾類的實(shí)例方法、靜態(tài)方法户魏、代碼塊挪挤;
synchronized 保護(hù)實(shí)例方法,實(shí)際保護(hù)的是同一個(gè)對(duì)象的方法調(diào)用鸠信,當(dāng)為不同對(duì)象時(shí)论寨,多線程是可以同時(shí)訪問同一個(gè) synchronized 方法的;
synchronized 靜態(tài)方法和 synchronized 實(shí)例方法保護(hù)的是不同的對(duì)象绰垂。不同的兩個(gè)線程可以同時(shí)一個(gè)執(zhí)行 synchronized 靜態(tài)方法火焰,另一個(gè)執(zhí)行 synchronized 實(shí)例方法。因?yàn)?synchronized 靜態(tài)方法保護(hù)的是 class 類對(duì)象占业,synchronized 實(shí)例方法保護(hù)的是 this 實(shí)例對(duì)象;synchronized 代碼塊同步的可以是任何對(duì)象南蹂,因?yàn)槿魏螌?duì)象都有一個(gè)鎖和等待隊(duì)列念恍。
synchronized 具備可重入性,對(duì)同一個(gè)線程在獲得鎖之后再調(diào)用其他需要同樣鎖的代碼時(shí)可以直接調(diào)用仗考,其可重入性是通過記錄鎖的持有線程和持有數(shù)量來實(shí)現(xiàn)的词爬,調(diào)用 synchronized 代碼時(shí)檢查對(duì)象是否已經(jīng)被鎖权均,是則檢查是否被當(dāng)前線程鎖定,計(jì)數(shù)加一恋沃,不是則加入等待隊(duì)列必指,釋放時(shí)計(jì)數(shù)減一直到為零釋放鎖。
synchronized 還具備內(nèi)存可見性梅割,除了實(shí)現(xiàn)原子操作避免靜態(tài)以外對(duì)于明顯是原子操作的方法(譬如一個(gè) boolean 狀態(tài)變量 state 的 get 和 set 方法)也可以通過 synchronized 來保證并發(fā)的可見性葛家,在釋放鎖時(shí)所有寫入都會(huì)寫回內(nèi)存,而獲得鎖后都會(huì)從內(nèi)存讀取最新數(shù)據(jù)癞谒;不過對(duì)于已經(jīng)是原子性的操作為了保證內(nèi)存可見性而使用 synchronized 的成本會(huì)比較高,輕量級(jí)的選擇應(yīng)該是使用 volatile 修飾弹砚,一旦修飾,java 就會(huì)在操作對(duì)應(yīng)變量時(shí)插入特殊指令保證可見性殊校。
synchronized 是重量級(jí)鎖读存,其語義底層是通過一個(gè) monitor 監(jiān)視器對(duì)象來完成呕屎,其實(shí) wait敬察、notify 等方法也依賴于 monitor 對(duì)象,所以這就是為什么只有在同步的塊或者方法中才能調(diào)用 wait蹂安、notify 等方法锐帜,否則會(huì)拋出 IllegalMonitorStateException 異常的原因,監(jiān)視器鎖(monitor)的本質(zhì)依賴于底層操作系統(tǒng)的互斥鎖(Mutex Lock)實(shí)現(xiàn)允瞧,而操作系統(tǒng)實(shí)現(xiàn)線程之間的切換需要從用戶態(tài)轉(zhuǎn)換到核心態(tài)蛮拔,這個(gè)成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對(duì)比較長(zhǎng)的時(shí)間畦韭,所以這就是為什么 synchronized 效率低且重量級(jí)的原因(Java 1.6 進(jìn)行了優(yōu)化肛跌,但是相比其他鎖機(jī)制還是略顯偏重)。
問:synchronized 與 lock 的區(qū)別转唉?
答:
synchronized 在發(fā)生異常時(shí)會(huì)自動(dòng)釋放線程占用的鎖資源西饵,Lock 需要在發(fā)生異常時(shí)主動(dòng)釋放;
synchronized 在鎖等待狀態(tài)下無法響應(yīng)中斷期虾,而 Lock 可以驯嘱。