在Java併發實現的機制中,大部分的容器和框架都是依賴於volatile/synchronized/原子操做實現的,瞭解底層的併發機制,對於併發編程會帶來不少幫助編程
synchronized在多線程併發編程中已是一個元老級的存在,一般被稱做是重量級鎖。既然是經常使用的一種鎖,那麼就須要對它的底層實現有深刻的瞭解。緩存
當一個線程在訪問同步代碼塊時,就必需要先獲取該代碼塊中對象的鎖,退出或者拋出異常時,就必需要釋放鎖。synchronized的同步實現,是JVM基於進入和退出同步對象的Monitor對象來實現方法同步和代碼塊同步的。
每一個Monitor對象都有與之關聯的monitor,當且僅當monitor被持有後,它纔會處於鎖定狀態。同步代碼是使用monitorenter和monitorexit指令實現的。monitorenter指令時在代碼編譯後插入到同步代碼塊的開始位置,monitorexit指令則是插入到方法的結束和異常處。當線程執行到monitorenter時,會嘗試去獲取對象的monitor的全部權,即嘗試獲取對象的鎖。多線程
鎖共有四種狀態:無鎖狀態->偏向鎖狀態->輕量級鎖狀態->重量級鎖狀態併發
偏向鎖:當線程訪問同步塊並獲取鎖時,會在對象頭和棧針中的鎖記錄中存儲鎖偏向的ID,之後該線程在進入和退出同步塊時就不須要在使用CAS操做來進行加鎖和解鎖操做,而僅僅須要測試一下對象頭中的Mark Word字段中存儲的線程ID是不是當前線程便可。若是是,那麼表示獲取鎖成功;若是不是,則須要檢查對象頭中偏向鎖的字段是否設置爲了1(表示當前鎖是偏向鎖),若是沒有設置,則須要使用CAS來競爭獲取鎖,若是已經設置了,則嘗試使用CAS將對象頭的偏向鎖ID指向當前線程。
輕量級鎖;框架
鎖類型 | 優勢 | 缺點 | 適用場景 |
---|---|---|---|
重量級鎖 | 線程競爭不使用自旋,不會浪費CPU | 線程阻塞,響應時間慢 | 追求吞吐量,同步塊執行的時間長的場景 |
輕量級鎖 | 競爭的線程不會阻塞,提升程序的響應速度 | 若是線程長時間得不到鎖,那麼自旋就會浪費CPU | 追求響應時間,同步塊執行速度快 |
偏向鎖 | 加鎖和解鎖不須要額外的資源消耗 | 若是線程間存在競爭,會帶來額外的鎖撤銷的消耗 | 適用於只有一個線程訪問同步場景 |
volatile是輕量級的synchronized,volatile在多線程開發中保證了共享變量的可見性,所謂可見性,指的是當一個線程修改了共享變量以後,對於其餘線程來講,可以讀到這個修改的值。編程語言
volatile定義:Java編程語言容許線程訪問共享變量,爲了確保共享變量能被準確和一致地更新,線程應該確保經過排他鎖得到這個變量。
實現原理:當對聲明瞭volatile的變量進行寫操做時,JVM就會向處理器發送一條帶有lock前綴的指令,將這個變量所在的緩存行的數據寫回到系統內存中。同時,在多處理器的狀況下,須要執行緩存一致性協議,即每一個處理器都須要經過嗅探總線上傳播的數據來檢查本身的緩存是否過時,當處理器發現本身緩存行對應的內存地址被修改,就會將當前處理器的緩存行設置爲無效,當處理器須要對這個數據進行操做時,再從系統內存中把數據讀取到緩存行中。測試
見文章[併發編程系列]Java中的原子操做類線程