volatile和synchronized與lock的理解


volatile

特征:

a:可見性:一個線程修改了某個共享變量的值,其他線程能夠立馬得知這個修改。

b:禁止特定的處理器重排序。

volatile的內存語義:

1.當寫一個volatile變量的時候,jmm會把本地內存中的共享變量刷新到主內存。

2.當讀一個volatile變量的是時候,jmm會把線程本地內存的值設置為無效,然后從主內存中讀取共享變量。

volatile的重排序有三個規則:

1.當第二個操作為volatile寫的時候,第一個操作不管是什么,都不允許重排序。

2.當第一個操作為volatile讀的時候,第二個操作不管是什么,都不允許重排序。

3.當第一個操作為volatile寫的時候,第二個操作是volatile讀的時候,不允許重排序。

除此以外的情況,都運行重排序。而重排序的實現是靠加入內存屏障來實現的。內存屏障時用來禁止特定的重排序的cpu指令。包括4中,loadload,store store,store load與load/store。load可以理解為讀操作,store可以理解為寫操作,舉例說明,loadload是保證在第二個load和其他一系列操作之前要確保第一個load的讀操作完成。store store是保證在第二個store及寫操作之前,第一個store寫操作對其他處理器可見。其中store load的開銷最大,是個萬能屏障,兼具其他三個屏障的功能。

 

synchronized和lock的底層實現和對比:

 synchronized是通過jvm原生實現的,其中可以分為給代碼塊加鎖,給方法加鎖,給靜態類加鎖。給代碼塊加鎖鎖住的是加鎖的對象,給方法加鎖鎖住的是對象實例,給靜態類加鎖鎖住的是整個類。

jvm通過進入和退出monitor來實現代碼塊和方法的同步。其中給代碼塊加鎖可以理解為在方法的入口和出口分別加入了monitorenter和monitorexit字節碼指令來實現的。必須要保證有一個monitorenter對應一個monitorexit,進入到monitorenter就表示拿到了相應的鎖。java1.6之前synchronized可以說是重量級鎖,1.6之后對synchronized做出的優化使得synchronized沒有那么重量級了,加入了鎖粗化,自旋鎖和自適應自旋,鎖消除,輕量級鎖,偏向鎖。

鎖粗化是對對一個代碼塊家了很多鎖,由於要不停的進入和出去加大的開銷,可以把一部分聯系緊密的代碼塊合並為一個鎖或者少量的鎖,使鎖的力度變粗。

鎖消除是當代碼塊中有鎖但是檢測到不存在競爭沒有必要加鎖的時候就把鎖去掉。

自旋鎖:同步的時候阻塞會影響性能,掛起線程和恢復線程的操作都需要轉入內核態來完成,這些操作給系統的並發性能帶來的很大的壓力。在很多共享數據的鎖定狀態之后持續很短的一段時間,為了這段時間去掛起和恢復線程並不值得。如果物理機上不止一個處理器,能讓兩個或以上的線程同時並行執行,我們可以讓后面請求鎖的線程“稍等一下”,但是不放棄cpu,看看持有鎖的線程是否很快就會釋放鎖。為了讓線程等待,我們只需讓線程執行一個等待,這就是自旋。自旋值默認是10。1.6中引入了自適應的自旋,如果前一個剛剛獲得過鎖,並且持有鎖的線程還在進行中,那么虛擬機會認為下一次自旋也有可能成功,進而允許自旋等待更長時間。對於很少獲得的鎖,直接放棄自旋,避免資源浪費,直接掛起線程。

輕量級鎖:輕量級鎖是相對於重量級鎖而言的,它的本意是在沒有多線程競爭的前提下,減少傳統的重量級鎖使用操作系統互斥量產生的性能消耗。在進入代碼塊的時候,如果此同步對象沒有被鎖定,也就是鎖標志位是01狀態,虛擬機首先在當前線程的棧幀上建立一個鎖記錄(lock record),用於存儲Mark world的拷貝,然后虛擬機將使用cas操作嘗試將對象的Mark world更新指向lock record的指針。更新成功了那么該線程就擁有了鎖,並且對象的鎖標志位將裝換為00,即表示此對象處於輕量級鎖的狀態。更新失敗了,虛擬機首先檢查Mark world是否指向lock record,是的話說明當前線程已經擁有了這個對象的鎖,那就直接進去代碼塊繼續執行,否則說明鎖對象已經被其他線程搶占了。如果有兩個以上的對象爭用一個鎖,那么輕量級鎖不再有效,升級為重量級鎖,鎖狀態變為10,mark word中存儲的就是重量級鎖的指針。

偏向鎖:如果說輕量級鎖是在無競爭的條件下使用cas操作去消除同步使用的互斥量,那么偏向鎖就是在無競爭的條件下把整個同步都省掉,連cas操作都不做了。偏向的偏,意思就是如果獲取了鎖,下一個獲取的話偏向由上一次獲取它的線程來獲取。當線程第一次獲取到鎖對象,狀態改寫為01,然后使用cas操作,把獲取到鎖的線程的id記錄在mark word中,如果cas成功,持有偏向鎖的線程以后每次進入這個鎖相關的同步塊時,虛擬機都可以不在進行任何同步操作。當有另外一個線程去嘗試獲取這個鎖時,偏向模式宣告結束。根據鎖對象目前是否處於鎖定的狀態,撤銷偏向后恢復到未鎖定或輕量級鎖定。

而lock是Java寫的,基於aqs框架,需要顯示的獲取鎖和釋放鎖,並且包含在try catch finally語句塊里面,同樣是可重入鎖,lock還提供了比synchronized更強大的一些功能。主要包括三點:

1.可中斷獲取鎖,獲取鎖的時候可以定時,如果過了這個時間還是沒有獲得鎖,那么就改為做其他事情。

2.可以綁定多個條件。synchronized里面,如果用wait、notify的話,實現的是隱含的一個條件,如果要和多於一個的條件綁定的話需要再創建鎖。而lock不需要,一個鎖可以產生多個condition,只需要通過lock的newcondition方法即可。

3.可以實現公平鎖。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM