Java之多線程里面的鎖理解以及synchronized與Lock的區(qū)別
一、宏觀的說下鎖的分類
1)鎖分為樂觀鎖、悲觀鎖
悲觀鎖認(rèn)為對于同一個數(shù)據(jù)的并發(fā)操作,一定是會發(fā)生修改的,哪怕沒有修改,也會認(rèn)為修改。因此對于同一個數(shù)據(jù)的并發(fā)操作,悲觀鎖采取加鎖的形式。悲觀的認(rèn)為,不加鎖的并發(fā)操作一定會出問題。
樂觀鎖則認(rèn)為對于同一個數(shù)據(jù)的并發(fā)操作,是不會發(fā)生修改的。在更新數(shù)據(jù)的時候,會采用嘗試更新,不斷重新的方式更新數(shù)據(jù)。樂觀的認(rèn)為,不加鎖的并發(fā)操作是沒有事情的
2)鎖分為公平鎖、非公平鎖
公平鎖是指多個線程按照申請鎖的順序來獲取鎖。
非公平鎖是指多個線程獲取鎖的順序并不是按照申請鎖的順序,有可能后申請的線程比先申請的線程優(yōu)先獲取鎖。有可能,會造成優(yōu)先級反轉(zhuǎn)或者饑餓現(xiàn)象。
3)鎖分為獨享鎖、共享鎖
獨享鎖是指該鎖一次只能被一個線程所持有。
共享鎖是指該鎖可被多個線程所持有。
二、java中常見具體高并發(fā)鎖
1)synchronized
synchronized機(jī)制是給共享資源上鎖,只有拿到鎖的線程才可以訪問共享資源,這樣就可以強(qiáng)制使得對共享資源的訪問都是順序的,夠保證在同一個時刻最多只有一個線程執(zhí)行同一個對象的同步代碼,可保證修飾的代碼在執(zhí)行過程中不會被其他線程干擾
synchronized(obj) {
}
synchronized實現(xiàn)的機(jī)理依賴于軟件層面上的JVM,對于Synchronized而言,也是一種悲觀鎖、非公平鎖、也是獨享鎖、也是互斥鎖。
2)ReentrantLock
可重入鎖,顧名思義,這個鎖可以被線程多次重復(fù)進(jìn)入進(jìn)行獲取操作
Lock實現(xiàn)的機(jī)理依賴于特殊的CPU指令,比如執(zhí)行l(wèi)ock()方法的時候,cpu發(fā)出lock指令,比如我們執(zhí)行unlock的時候,cpu發(fā)出lock指令,可以認(rèn)為不受JVM的約束,并可以通過其他語言平臺來完成底層的實現(xiàn)。在并發(fā)量較小的多線程應(yīng)用程序中,ReentrantLock與synchronized性能相差無幾,但在高并發(fā)量的條件下,synchronized性能會迅速下降幾十倍,而ReentrantLock的性能卻能依然維持一個水準(zhǔn),高并發(fā)量情況下使用ReentrantLock。
ReentrantLock通過方法lock()與unlock()來進(jìn)行加鎖與解鎖操作,與synchronized會被JVM自動解鎖機(jī)制不同,ReentrantLock加鎖后需要手動進(jìn)行解鎖。為了避免程序出現(xiàn)異常而無法正常解鎖的情況,使用ReentrantLock必須在finally控制塊中進(jìn)行解鎖操作。通常使用方式如下所示
Lock lock = new ReentrantLock();
try {
lock.lock();
}
finally {
lock.unlock();
}
對于ReentrantLock而言,ReentrantLock在構(gòu)造函數(shù)中提供了是否公平鎖的初始化方式,默認(rèn)為非公平鎖。這是因為,非公平鎖實際執(zhí)行的效率要遠(yuǎn)遠(yuǎn)超出公平鎖、ReentrantLock也是互斥鎖、也是獨享鎖。
3)Semaphore
互斥是進(jìn)程同步關(guān)系的一種特殊情況,相當(dāng)于只存在一個臨界資源,因此同時最多只能給一個線程提供服務(wù)。但是,在實際復(fù)雜的多線程應(yīng)用程序中,可能存在多個臨界資源,這時候我們可以借助Semaphore信號量來完成多個臨界資源的訪問
,通過acquire()與release()方法來獲得和釋放臨界資源,Semaphore和ReentrantLock用法差不多,Semaphore的鎖釋放操作也由手動進(jìn)行,因此與ReentrantLock一樣,為避免線程因拋出異常而無法正常釋放鎖的情況發(fā)生,釋放鎖的操作也必須在finally代碼塊中完成,
構(gòu)造方法里面也可以設(shè)置否公平鎖的初始化方式,默認(rèn)為非公平鎖。
4)AtomicInteger
在多線程程序中,諸如++i或i++等運(yùn)算不具有原子性,是不安全的線程操作之一。通常我們會使用synchronized將該操作變成一個原子操作,但JVM為此類操作特意提供了一些同步類,使得使用更方便,且使程序運(yùn)行效率變得更高。通常AtomicInteger的性能是ReentantLock的好幾倍。
三、各個鎖的優(yōu)勢
1.synchronized:
在資源競爭不是很激烈的情況,偶爾會有同步的情形下,synchronized是很合適的。原因在于,編譯程序通常會盡可能的進(jìn)行優(yōu)化synchronize,另外可讀性非常好,synchronized它是通過悲觀鎖實現(xiàn)的。
2.ReentrantLock:
在資源競爭不激烈的情形下,性能稍微比synchronized差點點。但是當(dāng)同步非常激烈的時候,synchronized的性能一下子能下降好幾十倍,而ReentrantLock確還能維持常態(tài)。
高并發(fā)量情況下使用ReentrantLock。
3.Atomic:
和上面的類似,不激烈情況下,性能比synchronized略遜,而激烈的時候,也能維持常態(tài)。激烈的時候,Atomic的性能會優(yōu)于ReentrantLock一倍左右。但是其有一個缺點,就是只能同步一個值,一段代碼中只能出現(xiàn)一個Atomic的變量,多于一個同步無效。因為他不能在多個Atomic之間同步,是基于cas操作來實現(xiàn)的,它是通過樂觀鎖來實現(xiàn)的。
參考地址:https://youzhixueyuan.com/4-kinds-of-java-thread-locks.html 然后自己再對比比較和精簡分析
4 synchronized與Lock的區(qū)別
1).首先synchronized是java內(nèi)置關(guān)鍵字,是在在jvm層面,Lock是一個接口,最后是由CPU來發(fā)送lock和unlock指令,這個和volatile底層原理實現(xiàn)類似。
2).synchronized無法判斷是否獲取鎖的狀態(tài),Lock可以判斷是否獲取到鎖;
3 ) .synchronized會自動釋放鎖(a 線程執(zhí)行完同步代碼會釋放鎖 ;b 線程執(zhí)行過程中發(fā)生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖;
4) .用synchronized關(guān)鍵字的兩個線程1和線程2,如果當(dāng)前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,線程可以不用一直等待就結(jié)束了;
5) .synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可)
6) .Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。
作者:chen.yu
深信服三年半工作經(jīng)驗,目前就職游戲廠商,希望能和大家交流和學(xué)習(xí),
微信公眾號:編程入門到禿頭 或掃描下面二維碼
零基礎(chǔ)入門進(jìn)階人工智能(鏈接)