volatile既然不足以保證數據同步,那么就必須要引入鎖來確保。互斥鎖是最常見的同步手段,在并發過程中,當多條線程對同一個共享數據競爭時,它保證共享數據同一時刻只能被一條線程使用,其他線程只有等到鎖釋放后才能重新進行競爭。對于java開發人員,我們最熟悉的肯定就是用synchronized關鍵詞完成鎖功能,在涉及到多線程并發時,對于一些變量,你應該會毫不猶豫地加上synchronized去保證變量的同步性。
在C/C++可直接使用操作系統提供的互斥鎖實現同步和線程的阻塞和喚起,與之不同的是,java要把這些底層封裝,而synchronized就是一個典型的互斥鎖,同時它也是一個JVM級別的鎖,它的實現細節全部封裝在JVM中實現,對開發人員只提供了synchronized關鍵詞。根據鎖的顆粒度,可以用synchronized對一個變量、一個方法、一個對象和一個類等加鎖。被synchronized修飾的程序塊經過編譯后,會在前后生成monitorenter和monitorexit兩個字節碼指令,其中涉及到鎖定和解鎖對象的確定,這就要根據synchronized來確定了,假如明確指定了所對象,例如synchronized(變量)、synchronized(this)等,說明加解鎖對象為變量或運行時對象。假如沒有明確指定對象,則根據synchronized修飾的方法去找對應的鎖對象,如修飾一個非靜態方法表示此方法對應的對象為鎖對象,如修飾一個靜態方法則表示此方法對應的類對象為鎖對象。當一個對象被鎖住時,對象里面所有用synchronized修飾的方法都將產生堵塞,而對象里非synchronized修飾的方法可正常被調用,不收鎖影響。
為了實現互斥鎖,JVM的monitorenter和monitorexit字節碼依賴底層操作系統的互斥鎖來實現,java層面的線程與操作系統的原生線程有映射關系,這時如果要將一個線程進行阻塞或喚起都需要操作系統的協助,這就需要從用戶態切換到內核態來執行,這種切換代價十分昂貴,需要消耗很多處理器時間。如果可能,應該減少這樣的切換,jvm一般會采取一些措施進行優化,例如在把線程進行阻塞操作之前先讓線程自旋等待一段時間,可能在等待期間其他線程已經解鎖,這時就無需再讓線程執行阻塞操作,避免了用戶態到內核態的切換。
Synchronized還有另外一個重要的特性——可重入性。這個特性主要是針對當前線程而言的,可重入即是自己可以再次獲得自己的內部鎖,在嘗試獲取對象鎖時,如果當前線程已經擁有了此對象的鎖,則把鎖的計數器加一,在釋放鎖時則對應地減一,當鎖計數器為0時表示鎖完全被釋放,此時其他線程可對其加鎖。可重入特性是為了解決自己鎖死自己的情況,如下面偽代碼:
public class DeadLock{
public synchronized void method1(){}
public synchronized void method2(){
this.method1();
}
public static void main(String[] args){
??DeadLock deadLock=new DeadLock();
?? deadLock.method2();
}
}
這種情況其實也并非不常見,一個類中的同步方法調用另一個同步方法,假如synchronized不支持重入,進入method2方法時當前線程將嘗試獲取deadLock對象的鎖,而method2方法里面執行method1方法時,當前線程又要去嘗試獲取deadLock對象的鎖,這時由于不支持重入,它要去等deadLock對象的鎖釋放,把自己阻塞了,這就是自己鎖死自己的現象。所以重入機制的引入,杜絕了這種情況的發生。
synchronized實現的是一個非公平鎖,非公平主要表現在獲取鎖的行為上,并非是按照申請鎖的時間前后給等待線程分配鎖的,每當鎖被釋放后,任何一個線程都有機會競爭到鎖,這樣做的目的是為了提高執行性能,當然也會產生線程饑餓現象。
synchronized最后一個特性(缺點)就是不可中斷性,在所有等待的線程中,你們唯一能做的就是等,而實際情況可能是有些任務等了足夠久了,我要取消此任務去干別的事情,此時synchronized是無法幫你實現的,它把所有實現機制都交給了JVM,提供了方便的同時也體現出了自己的局限性。
這節主要介紹java的synchronized關鍵詞,包括它的作用及JVM及操作系統的底層實現,它的可重入性和不可中斷性,它實現的是一個非公平的互斥鎖,同時它也是一個悲觀的并發策略,不管是否會產生競爭,任何的數據操作都必須要加鎖。對synchronized深入全面的理解對理解tomcat中跟多線程并發相關的模塊是很有幫助的。
喜歡研究java的同學可以交個朋友,下面是本人的微信號:

- 前言
- 悲觀的并發策略——Synchronized互斥鎖
- 樂觀的并發策略——基于CAS的自旋
- Java并發框架——AQS之原子性如何保證?
- Java并發框架——AQS之如何使用AQS構建同步器
- Java多線程模型
- Java并發框架——什么是AQS框架
- Java多線程的調度策略
- Java并發框架——AQS中斷的支持
- Java線程狀態
- 多線程之Java線程阻塞與喚醒
- Java并發框架——AQS之阻塞與喚醒
- Java并發框架——AQS阻塞隊列管理(一)——自旋鎖
- Java并發框架——AQS阻塞隊列管理(二)——自旋鎖優化
- Java并發框架——AQS阻塞隊列管理(三)——CLH鎖改造
- Java并發框架——AQS超時機制
- Java并發框架——同步狀態的管理
- Java并發框架——公平性
- Java并發——線程池原理