天天看點

Java之多線程裡面的鎖了解以及synchronized與Lock的差別

一、宏觀的說下鎖的分類

1)鎖分為樂觀鎖、悲觀鎖

悲觀鎖認為對于同一個資料的并發操作,一定是會發生修改的,哪怕沒有修改,也會認為修改。是以對于同一個資料的并發操作,悲觀鎖采取加鎖的形式。悲觀的認為,不加鎖的并發操作一定會出問題。

樂觀鎖則認為對于同一個資料的并發操作,是不會發生修改的。在更新資料的時候,會采用嘗試更新,不斷重新的方式更新資料。樂觀的認為,不加鎖的并發操作是沒有事情的

2)鎖分為公平鎖、非公平鎖

公平鎖是指多個線程按照申請鎖的順序來擷取鎖。

非公平鎖是指多個線程擷取鎖的順序并不是按照申請鎖的順序,有可能後申請的線程比先申請的線程優先擷取鎖。有可能,會造成優先級反轉或者饑餓現象。

3)鎖分為獨享鎖、共享鎖

獨享鎖是指該鎖一次隻能被一個線程所持有。

共享鎖是指該鎖可被多個線程所持有。

二、java中常見具體高并發鎖

1)synchronized

synchronized機制是給共享資源上鎖,隻有拿到鎖的線程才可以通路共享資源,這樣就可以強制使得對共享資源的通路都是順序的,夠保證在同一個時刻最多隻有一個線程執行同一個對象的同步代碼,可保證修飾的代碼在執行過程中不會被其他線程幹擾

synchronized(obj) {

}

synchronized實作的機理依賴于軟體層面上的JVM,對于Synchronized而言,也是一種悲觀鎖、非公平鎖、也是獨享鎖、也是互斥鎖。

2)ReentrantLock

可重入鎖,顧名思義,這個鎖可以被線程多次重複進入進行擷取操作

Lock實作的機理依賴于特殊的CPU指令,比如執行lock()方法的時候,cpu發出lock指令,比如我們執行unlock的時候,cpu發出lock指令,可以認為不受JVM的限制,并可以通過其他語言平台來完成底層的實作。在并發量較小的多線程應用程式中,ReentrantLock與synchronized性能相差無幾,但在高并發量的條件下,synchronized性能會迅速下降幾十倍,而ReentrantLock的性能卻能依然維持一個水準,高并發量情況下使用ReentrantLock。

ReentrantLock通過方法lock()與unlock()來進行加鎖與解鎖操作,與synchronized會被JVM自動解鎖機制不同,ReentrantLock加鎖後需要手動進行解鎖。為了避免程式出現異常而無法正常解鎖的情況,使用ReentrantLock必須在finally控制塊中進行解鎖操作。通常使用方式如下所示

Lock lock = new ReentrantLock();
 
try {
 
    lock.lock();
 
}
 
finally {
 
    lock.unlock();
 
}      

對于ReentrantLock而言,ReentrantLock在構造函數中提供了是否公平鎖的初始化方式,預設為非公平鎖。這是因為,非公平鎖實際執行的效率要遠遠超出公平鎖、ReentrantLock也是互斥鎖、也是獨享鎖。

3)Semaphore

互斥是程序同步關系的一種特殊情況,相當于隻存在一個臨界資源,是以同時最多隻能給一個線程提供服務。但是,在實際複雜的多線程應用程式中,可能存在多個臨界資源,這時候我們可以借助Semaphore信号量來完成多個臨界資源的通路

,通過acquire()與release()方法來獲得和釋放臨界資源,Semaphore和ReentrantLock用法差不多,Semaphore的鎖釋放操作也由手動進行,是以與ReentrantLock一樣,為避免線程因抛出異常而無法正常釋放鎖的情況發生,釋放鎖的操作也必須在finally代碼塊中完成,

構造方法裡面也可以設定否公平鎖的初始化方式,預設為非公平鎖。

4)AtomicInteger

在多線程程式中,諸如++i或i++等運算不具有原子性,是不安全的線程操作之一。通常我們會使用synchronized将該操作變成一個原子操作,但JVM為此類操作特意提供了一些同步類,使得使用更友善,且使程式運作效率變得更高。通常AtomicInteger的性能是ReentantLock的好幾倍。

三、各個鎖的優勢

1.synchronized:

在資源競争不是很激烈的情況,偶爾會有同步的情形下,synchronized是很合适的。原因在于,編譯程式通常會盡可能的進行優化synchronize,另外可讀性非常好,synchronized它是通過悲觀鎖實作的。

2.ReentrantLock:

在資源競争不激烈的情形下,性能稍微比synchronized差點點。但是當同步非常激烈的時候,synchronized的性能一下子能下降好幾十倍,而ReentrantLock确還能維持常态。

高并發量情況下使用ReentrantLock。

3.Atomic:

和上面的類似,不激烈情況下,性能比synchronized略遜,而激烈的時候,也能維持常态。激烈的時候,Atomic的性能會優于ReentrantLock一倍左右。但是其有一個缺點,就是隻能同步一個值,一段代碼中隻能出現一個Atomic的變量,多于一個同步無效。因為他不能在多個Atomic之間同步,是基于cas操作來實作的,它是通過樂觀鎖來實作的。

參考位址:

https://youzhixueyuan.com/4-kinds-of-java-thread-locks.html

 然後自己再對比比較和精簡分析

4 synchronized與Lock的差別

1).首先synchronized是java内置關鍵字,是在在jvm層面,Lock是一個接口,最後是由CPU來發送lock和unlock指令,這個和volatile底層原理實作類似。

2).synchronized無法判斷是否擷取鎖的狀态,Lock可以判斷是否擷取到鎖;

3 ) .synchronized會自動釋放鎖(a 線程執行完同步代碼會釋放鎖 ;b 線程執行過程中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖;

4) .用synchronized關鍵字的兩個線程1和線程2,如果目前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試擷取不到鎖,線程可以不用一直等待就結束了;

5) .synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可)

6) .Lock鎖适合大量同步的代碼的同步問題,synchronized鎖适合代碼少量的同步問題。