1)鎖消除java
概念:JVM在JIT編譯(即時編譯)時,經過對運行上下文的掃描,去除掉那些不可能發生共享資源競爭的鎖,從而節省了線程請求這些鎖的時間。 舉例: StringBuffer的append方法是一個同步方法,若是StringBuffer類型的變量是一個局部變量,則該變量就不會被其它線程所使用,即對局部變量的操做是不會發生線程不安全的問題。 在這種情景下,JVM會在JIT編譯時自動將append方法上的鎖去掉。
2)鎖粗化安全
概念:將多個連續的加鎖、解鎖操做鏈接在一塊兒,擴展成一個範圍更大的鎖,即將加鎖的粒度放大。 舉例:在for循環裏的加鎖/解鎖操做,通常須要放到for循環外。
3)使用偏向鎖和輕量級鎖多線程
說明: 1)java6爲了減小獲取鎖和釋放鎖帶來的性能消耗,引入了偏向鎖和輕量級鎖。 2)鎖一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖、輕量級鎖、重量級鎖。 3)鎖的狀態會隨着競爭狀況逐漸升級,而且只能夠升級而不能降級。 【偏向鎖】 1)背景:大多數狀況下,鎖不只不存在多線程競爭,並且老是由同一線程屢次得到,爲了讓線程得到鎖的代價更低而引入了偏向鎖。 2)概念:核心思想就是鎖會偏向第一個獲取它的線程,若是在接下來的執行過程當中沒有其它的線程獲取該鎖,則持有偏向鎖的線程永遠不須要同步。 3)目的:偏向鎖其實是一種優化鎖,其目的是爲了減小數據在無競爭狀況下的性能損耗。 4)原理: 1>當一個線程訪問同步塊並獲取鎖時,會在對象頭和棧幀中的鎖記錄裏存儲鎖偏向的線程ID。 2>之後該線程在進入和退出同步塊時就不須要進行CAS操做來加鎖和解鎖,只需簡單地判斷一下對象頭的Mark Word裏是否存儲着指向當前線程的偏向鎖。 5)偏向鎖的獲取: 1>訪問Mark Word中偏向鎖的標識位是否爲1,若是是1,則肯定爲偏向鎖。 說明: [1]若是偏向鎖的標識位爲0,說明此時是處於無鎖狀態,則當前線程經過CAS操做嘗試獲取偏向鎖,若是獲取鎖成功,則將Mark Word中的偏向線程ID設置爲當前線程ID;而且將偏向標識位設爲1。 [2]若是偏向鎖的標識位不爲1,也不爲0(此時偏向鎖的標識位沒有值),說明發生了競爭,偏向鎖已經膨脹爲輕量級鎖,這時使用CAS操做嘗試得到鎖。 2>若是是偏向鎖,則判斷Mark Word中的偏向線程ID是否指向當前線程,若是偏向線程ID指向當前線程,則代表當前線程已經獲取到了鎖; 3>若是偏向線程ID並未指向當前線程,則經過CAS操做嘗試獲取偏向鎖,若是獲取鎖成功,則將Mark Word中的偏向線程ID設置爲當前線程ID; 4>若是CAS獲取偏向鎖失敗,則表示有競爭。當到達全局安全點時(在這個時間點上沒有正在執行的字節碼),得到偏向鎖的線程被掛起,偏向鎖升級爲輕量級鎖,而後被阻塞在安全點的線程繼續往下執行同步代碼。 6)偏向鎖的釋放: 1>當其它的線程嘗試獲取偏向鎖時,持有偏向鎖的線程纔會釋放偏向鎖。 2>釋放偏向鎖須要等待全局安全點(在這個時間點上沒有正在執行的字節碼)。 3>過程: 首先暫停擁有偏向鎖的線程,而後檢查持有偏向鎖的線程是否活着,若是線程不處於活動狀態,則將對象頭設置成無鎖狀態, 若是線程還活着,說明此時發生了競爭,則偏向鎖升級爲輕量級鎖,而後剛剛被暫停的線程會繼續往下執行同步代碼。 7)優勢:加鎖和解鎖不須要額外的消耗,和執行非同步方法相比僅存在納秒級的差距 8)缺點:若是線程間存在鎖競爭,鎖撤銷會帶來額外的消耗。 9)說明: 1)偏向鎖默認在應用程序啓動幾秒鐘以後才激活。 2)能夠經過設置 -XX:BiasedLockingStartupDelay=0 來關閉延遲。 3)能夠經過設置 -XX:-UseBiasedLocking=false 來關閉偏向鎖,程序默認會進入輕量級鎖狀態。(若是應用程序裏的鎖大多狀況下處於競爭狀態,則應該將偏向鎖關閉) 【輕量級鎖】 1)原理: 1>當使用輕量級鎖(鎖標識位爲00)時,線程在執行同步塊以前,JVM會先在當前線程的棧楨中建立用於存儲鎖記錄的空間,並將對象頭中的Mark Word複製到鎖記錄中(注:鎖記錄中的標識字段稱爲Displaced Mark Word)。 2>將對象頭中的MarkWord複製到棧楨中的鎖記錄中以後,虛擬機將嘗試使用CAS將對象頭中Mark Word替換爲指向該線程虛擬機棧中鎖記錄的指針,此時若是沒有線程佔有鎖或者沒有線程競爭鎖,則當前線程成功獲取到鎖,而後執行同步塊中的代碼。 3>若是在獲取到鎖的線程執行同步代碼的過程當中,另外一個線程也完成了棧楨中鎖記錄的建立,而且已經將對象頭中的MarkWord複製到了本身的鎖記錄中,而後嘗試使用CAS將對象頭中的MarkWord修改成指向本身的鎖記錄的指針,可是因爲以前獲取到鎖的線程已經將對象頭中的MarkWord修改過了(而且如今還在執行同步體中的代碼,即仍然持有着鎖),因此此時對象頭中的MarkWord與當前線程鎖記錄中MarkWord的值不一樣,致使CAS操做失敗,而後該線程就會不停地循環使用CAS操做試圖將對象頭中的MarkWord替換爲本身鎖記錄中MarkWord的值,(當循環次數或循環時間達到上限時中止循環)若是在循環結束以前CAS操做成功,那麼該線程就能夠成功獲取到鎖,若是循環結束以後依然獲取不到鎖,則鎖獲取失敗,對象頭中的MarkWord會被修改成指向重量級鎖的指針,而後這個獲取鎖失敗的線程就會被掛起,阻塞了。 4>當持有鎖的那個線程執行完同步體以後,使用CAS操做將對象頭中的MarkWord還原爲最初的狀態時(將對象頭中指向鎖記錄的指針替換爲Displaced Mark Word ),發現MarkWord已被修改成指向重量級鎖的指針,所以CAS操做失敗,該線程會釋放鎖並喚起阻塞等待的線程,開始新一輪奪鎖之爭,而此時,輕量級鎖已經膨脹爲重量級鎖,全部競爭失敗的線程都會阻塞,而不是自旋。 自旋鎖: 1)所謂自旋鎖,就是讓沒有得到鎖的進程本身運行一段時間自循環(默認開啓),可是不掛起線程。 2)自旋的代價就是該線程會一直佔用處理器若是鎖佔用的時間很短,自旋等待的效果很好,反之,自旋鎖會消耗大量處理器資源。 3)所以,自旋的等待時間必須有必定限度,超過限度尚未得到鎖,就要掛起線程。 優勢:在沒有多線程競爭的前提下,減小傳統的重量級鎖帶來的性能損耗。 缺點:競爭的線程若是始終得不到鎖,自旋會消耗cpu。 應用:追求響應時間,同步塊執行速度很是快。 【重量級鎖】 說明: 1)java6以前的synchronized屬於重量級鎖,效率低下,由於monitor是依賴操做系統的Mutex Lock(互斥量)來實現的。 2)多線程競爭鎖時,會引發線程的上下文切換(即在cpu分配的時間片尚未用完的狀況下進行了上下文切換)。 3)操做系統實現線程的上下文切換須要從用戶態轉換到核心態,這個狀態之間的轉換須要相對較長的時間,時間成本相對較高。 4)在互斥狀態下,沒有獲得鎖的線程會被掛起阻塞,而掛起線程和恢復線程的操做都須要從用戶態轉入內核態中完成。 優勢:線程競爭不使用自旋,不會消耗cpu。 缺點:線程阻塞,響應時間緩慢。 應用:追求吞吐量,同步塊執行速度較長