jdk 1.6 對併發性進行了很大的改進,這也是為了使執行緒之間更好更高效地共享資料,解決競爭問題,實現執行緒安全。因此從 jdk 1.6 開始,實現了很多鎖的優化技術。講正題之前,先說一下 reentrantlock 和 synchronized 這對冤家,我們經常會拿這兩個鎖作比較,其中乙個是顯式鎖,實現於 lock 介面;而另外乙個是隱式鎖,更加的原生。
如果我們從效能上來比較的話,在 jdk 1.6 以前,多執行緒環境下的 synchronized 效能明顯差於 reentrantlock;但在 jdk 1.6 及其之後的版本中,兩者的效能已經基本持平,而且我們通常優先考慮使用 synchronized 進行同步。究其原因,就是「鎖優化」。
其實自旋鎖在 jdk 1.4.2 中已經引入,不過當時的預設狀態為關閉;在 jdk 1.6 中改為預設開啟。
在互斥同步中,阻塞對效能的影響是最大的,掛起執行緒和恢復執行緒兩個操作(即執行緒的切換)給了併發效能很大的壓力。
但是很多時候,共享資源處於鎖定狀態的時間其實非常短,為了那麼短的時間而去對執行緒反覆地掛起與恢復明顯十分不值得。因此我們可以利用自旋鎖避免這兩個操作。
當乙個執行緒在請求乙個被持有的鎖時,讓這個執行緒執行乙個空迴圈(自旋),此時並不會放棄處理器的執行,如果鎖很快就被釋放,那麼就避免了對這個執行緒的掛起與恢復操作。
自旋本身避免了執行緒切換帶來的開銷,但也占用了處理器的時間。如果鎖被占用的時間很短,那自旋鎖的效果自然很好;但如果時間很長,那麼這個自旋的執行緒就白白消耗了處理器的資源,反而適得其反,浪費了效能。
因此,自旋等待的時間是有限度的,一旦超過了自旋的限度次數,那麼就會使用傳統的方法進行阻塞,即掛起該執行緒。
jdk 1.6 中對自旋鎖進行了改進,引入了自適應自旋鎖,使得自旋的時間不再固定。簡單來說,就是隨著程式的執行和效能的監控,jvm 會對鎖的情況進行**,從而給出適合的自旋時間,更加 「智慧型」。
jvm 會對於一些**上要求同步,但被檢測到不可能存在共享資料競爭的鎖進行消除。
例子:
public void add(string str1, string str2)
複製**
通常情況下,我們編寫**時,都盡可能地將同步塊的作用範圍縮小,使得鎖的持有時間盡可能地縮短,提高細粒度,增加併發度,降低鎖的競爭。
但是有些情況下,如果一系列連續的操作中我們不斷地加鎖解鎖,比如在迴圈之中,那麼也會造成不必要的效能損耗。
比如:
public void add(string str1, string str2, string str3)
複製**
大多數情況下,鎖不僅不存在多執行緒競爭狀態,而且通常由同乙個執行緒多次獲得,因此,我們有必要減少同乙個執行緒多次獲得同乙個鎖的效能消耗。
當鎖物件第一次被執行緒獲取的時候,虛擬機器在物件的物件頭中標誌為偏向模式,同時使用 cas 操作把獲取到這個鎖的執行緒的 id 記錄在物件頭的 mark word 資料中。(這部分不了解的讀者可以去學習一下 jvm 的「物件記憶體布局」)
只要 cas 操作獲取成功,該鎖物件便 「偏向」 了這個執行緒,只要不出現第二個執行緒,這個鎖物件的物件頭就會一直記錄著該執行緒的 id。
這時,獲得偏向鎖的執行緒以後每次進入這個鎖的時候都不再需要進行同步操作,一路暢通。
那如果出現了第二個執行緒會發生什麼呢?我們繼續往下看。
當偏向鎖失效後,便會公升級為輕量鎖
當乙個執行緒企圖持有乙個鎖的時候,倘若這個鎖已經是偏向狀態,那麼這個時候會將偏向狀態解除,然後在競爭這個鎖的執行緒的棧幀中建立乙個鎖記錄的空間(lock record),並把鎖物件的 mark word 拷貝到裡面來,記作 displaced mark word。
然後,jvm 再使用 cas 操作將鎖物件的 mark word 更新為指向其中乙個執行緒的 lock record 的指標,當這個操作成功,這個執行緒也就持有了該輕量鎖。
當然,輕量鎖的持有和釋放,都需要 cas 操作進行。釋放鎖的時候,只需要把棧幀裡的 displaced markd word 使用 cas 複製回去即可。如果 cas 操作獲取鎖失敗,jvm 會首先檢查一下鎖物件的 mark word 是否指向當前執行緒,是則可以直接通行,否則先自旋一下吧。
這個鎖適應的是沒有競爭或是只有輕度競爭的情況,若是傳送了輕度的競爭,只需要進行幾次自旋即可。
但是一旦發生長時間的競爭,輕量級鎖就會公升級為重量級鎖,這時候就變成了傳統的通過阻塞來進行同步,並使用 monitor 物件來管理鎖的持有和釋放的方式(不要忘了 monitorenter 和 monitorexit 這兩個指令)。
簡單的說一說mmap
mmap memory map,就是記憶體對映 簡單的說就是將檔案對映到使用者的位址空間中。這麼做有什麼好處呢?1.傳統檔案訪問方式是,首先用open系統呼叫開啟檔案,然後使用read,write等呼叫進行順序或者隨即的i o.這種方式是非常低效的,每一次i o操作都需要一次系統呼叫.而通過mmap...
說一說JS的IIFE
iife immediately invoked function expression,意為立即呼叫的函式表示式,也就是說,宣告函式的同時立即呼叫這個函式。對比一下,這是不採用iife時的函式宣告和函式呼叫 function foo foo 下面是iife形式的函式呼叫 functionfoo 函...
說一說JS的IIFE
iife immediately invoked function expression,意為立即呼叫的函式表示式,也就是說,宣告函式的同時立即呼叫這個函式。對比一下,這是不採用iife時的函式宣告和函式呼叫 function foo window console.log a 2 js的模組就是函式...