a:可見性:一個線程修改了某個共享變量的值,其餘線程可以立馬得知這個修改。java
b:禁止特定的處理器重排序。多線程
volatile的內存語義:併發
1.當寫一個volatile變量的時候,jmm會把本地內存中的共享變量刷新到主內存。框架
2.當讀一個volatile變量的是時候,jmm會把線程本地內存的值設置爲無效,而後從主內存中讀取共享變量。jvm
volatile的重排序有三個規則:性能
1.當第二個操做爲volatile寫的時候,第一個操做不論是什麼,都不容許重排序。優化
2.當第一個操做爲volatile讀的時候,第二個操做不論是什麼,都不容許重排序。spa
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.能夠實現公平鎖。