程序有兩種不同的級別:用戶態(tài)、內核態(tài)
原來的操作系統(tǒng)中沒有這種級別之分,因此一個程序可能訪問其他程序的內存,就可能把整個機器都干掉。一般來說,操作系統(tǒng)跑在內核態(tài),內核態(tài)的程序可以訪問硬件(如內存,進行線程調度等),當用戶態(tài)的程序需要訪問硬件時,就需要向操作系統(tǒng)申請。在CPU硬件上分為ring0 - 1 - 2 -3,應用程序一般跑在ring3級,操作系統(tǒng)跑在ring0級,ring0級的程序可以直接訪問硬件而ring3級不行。
有輕量級鎖和重量級鎖之分,輕量級鎖都是在用戶態(tài)完成的,而重量級鎖需要進過操作系統(tǒng)。
1. CAS
CAS的全稱為CompareAndSwap,它是一條CPU并發(fā)原語。
功能:比較當前工作內存中的值和主內存中的值,如果相同則執(zhí)行規(guī)定操作。如果不相同則繼續(xù)比較直到工作內存中的值和主內存中的值相等為止。
CAS并發(fā)原語體現(xiàn)在JAVA語言中就是sun.misc.Unsafe類中的各個方法。調用Unsafe類中的CAS方法,JVM會幫我們實現(xiàn)出CAS的匯編指令(lock cmpxchg指令)。這是一種完全依賴于硬件的功能(鎖總線),通過它實現(xiàn)了原子操作。原語的執(zhí)行必須是連續(xù)的,在執(zhí)行的過程中不允許被中斷,也就是說CAS是一條CPU的原子指令,線程安全。
2.Unsafe類
是CAS的核心類,存在于sun.misc包中,其內部方法操作可以像C的指針一樣直接操作內存,所有方法都被native修飾。變量valueoffset,表示該變量值在內存中的偏移地址,Unsafe就根據(jù)內存的偏移地址獲取數(shù)據(jù),value被volatile修飾,保證了多線程之間的內存可見性。

不斷的循環(huán)看當前內存地址中的值是否是我期待的值,如果是則進行下面的操作,如果不是則繼續(xù)循環(huán)。
缺點:1. 循環(huán)時間長開銷大(如果CAS失敗,會一直進行嘗試)
? 2. 只能保證一個共享變量的原子操作
3. ABA問題
2. synchronized的鎖升級過程

Java SE 1.6為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了偏向鎖和輕量級鎖。
在1.6中,鎖一共有4中狀態(tài)
- 無鎖狀態(tài)
- 偏向鎖狀態(tài)
- 輕量級鎖狀態(tài)
- 重量級鎖狀態(tài)
- 偏向鎖意義:據(jù)調查,大多數(shù)情況下,鎖總是被同一線程調用,所以就出現(xiàn)了偏向鎖。
偏向鎖總是偏向于第一次獲取鎖的線程,一般無競爭狀態(tài)時使用的就是偏向鎖,可以使用-XX:+/-UseBiasedLocking參數(shù)啟用/關閉偏向鎖,默認是開啟的(線程數(shù)多的時候,適合關閉偏向鎖,因為競爭激烈,鎖會升級,關閉后可以省略升級過程,直接使用輕量級鎖)。
偏向鎖加鎖過程:
1)執(zhí)行到monitorenter(使用monitorenter和monitorexit兩個指令實現(xiàn)同步)時,首先檢查鎖標志是否為01(01表示未上鎖)
2)其次檢查偏向鎖位是否為0(0表示無鎖,1表示當前已是偏向鎖)
3)如果檢查成功,通過CAS操作,將Mark Word中的線程ID設置為自己的線程ID,然后將偏向鎖位設置為1
4)如果第二次仍是該線程進入同步區(qū),那么不再需要執(zhí)行第三步
5)如果第二次是其他線程進入,那么鎖升級
競爭激烈,多個線程同時爭鎖,鎖升級為輕量級鎖
第一個線程進入后設置為偏向鎖,第二次再來的線程不是該線程而是其他線程,鎖升級為輕量級鎖
?偏向鎖是默認啟用的,它會在程序啟動幾秒后才激活,可以通過JVM參數(shù):-XX:BiasedLockingStartupDelay=0關閉延遲。也可以使用參數(shù):-XX:UserBiasedLocking=false關閉偏向鎖
- 2.輕量級鎖
加鎖過程:
1)當某個線程想要占用這把鎖的時候,它會首先在自己的棧(幀)中創(chuàng)建一個鎖記錄(LockRecord)
2)然后將Mark Word信息復制(hashCode、分代年齡等)到LockRecord中
3)用CAS操作將Mark Word替換掉LR指針,即將Mark Word中除了標志位外的部分替換成一個指針
4)指向自己的LockRecord哪個線程替換成功,哪個線程就成功得到了鎖,輕量級鎖自旋次數(shù)超過10(任意一個線程),或者正在自旋的線程超過CPU核數(shù)一半,那么輕量級鎖升級為重量級鎖(jdk1.6以前,jdk1.6以后默認啟動自適應自旋),可以通過-XX:+PreBlockSpin指定自旋次數(shù)。
3.重量級鎖
?因為需要阻塞,并且需要向操作系統(tǒng)級別申請,所以說是重量級。重量級鎖是操作系統(tǒng)來決定由哪個線程來獲得鎖的:(用戶態(tài)到內核態(tài)的訪問)
1)生成或者復用monitor對象
2)Mark Word不再指向LR,而是指向monitor對象
3)線程阻塞,進入_EntryList排隊,由操作系統(tǒng)決定喚醒誰
4)操作系統(tǒng)喚醒線程后該線程的Mark Word替換為monitor pointer,然后執(zhí)行
monitor對象
?monitor可以理解為一個操作系統(tǒng)級別的互斥變量,當一個線程想要執(zhí)行一段被synchronized圈起來的同步方法或者代碼塊時,該線程得先獲取到synchronized修飾的對象對應的monitor。
在hotSpot虛擬機中,monitor是由ObjectMonitor實現(xiàn)的。其源碼是用c++來實現(xiàn)的
Java代碼里不會顯示地去創(chuàng)造這么一個monitor對象,我們也無需創(chuàng)建,事實上可以這么理解:我們是通過synchronized修飾符告訴JVM需要為我們的某個對象創(chuàng)建關聯(lián)的monitor對象。

整個過程:new對象的時候首先看是否開啟了偏向鎖,啟動了就匿名偏向(即沒有偏向任何線程),匿名偏向直接升級為偏向鎖,偏向鎖輕度競爭升級為輕量級鎖,再重度競爭則升級成重量級鎖。沒有啟動偏向鎖就生成普通對象,普通對象一般直接升級為輕量級鎖,輕量級鎖重度競爭升級成重量級鎖。
3.打開偏向鎖效率是否一定提高?為什么?
在明確知道有線程競爭的情況下,因為偏向鎖是存在競爭的,啟動偏向鎖不是一個合適的選擇。默認4s后啟動偏向鎖是因為JVM在啟動的時候一定會產生多線程的競爭。因此默認情況下延遲4s啟動偏向鎖。
4. 如何解決ABA問題
使用AtomicStampedReference,帶有時間戳(版本號)。