Java之多線程里面的鎖理解以及synchronized與Lock的區(qū)別

一、宏觀的說(shuō)下鎖的分類(lèi)

1)鎖分為樂(lè)觀鎖、悲觀鎖

悲觀鎖認(rèn)為對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,一定是會(huì)發(fā)生修改的,哪怕沒(méi)有修改,也會(huì)認(rèn)為修改。因此對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,悲觀鎖采取加鎖的形式。悲觀的認(rèn)為,不加鎖的并發(fā)操作一定會(huì)出問(wèn)題。
樂(lè)觀鎖則認(rèn)為對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,是不會(huì)發(fā)生修改的。在更新數(shù)據(jù)的時(shí)候,會(huì)采用嘗試更新,不斷重新的方式更新數(shù)據(jù)。樂(lè)觀的認(rèn)為,不加鎖的并發(fā)操作是沒(méi)有事情的


2)鎖分為公平鎖、非公平鎖
公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖。
非公平鎖是指多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序,有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取鎖。有可能,會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象。


3)鎖分為獨(dú)享鎖、共享鎖
獨(dú)享鎖是指該鎖一次只能被一個(gè)線程所持有。
共享鎖是指該鎖可被多個(gè)線程所持有。

 
二、java中常見(jiàn)具體高并發(fā)鎖

1)synchronized
synchronized機(jī)制是給共享資源上鎖,只有拿到鎖的線程才可以訪問(wèn)共享資源,這樣就可以強(qiáng)制使得對(duì)共享資源的訪問(wèn)都是順序的,夠保證在同一個(gè)時(shí)刻最多只有一個(gè)線程執(zhí)行同一個(gè)對(duì)象的同步代碼,可保證修飾的代碼在執(zhí)行過(guò)程中不會(huì)被其他線程干擾

    synchronized(obj) {
      
    }

synchronized實(shí)現(xiàn)的機(jī)理依賴于軟件層面上的JVM,對(duì)于Synchronized而言,也是一種悲觀鎖、非公平鎖、也是獨(dú)享鎖、也是互斥鎖。


    2)ReentrantLock
    可重入鎖,顧名思義,這個(gè)鎖可以被線程多次重復(fù)進(jìn)入進(jìn)行獲取操作
    Lock實(shí)現(xiàn)的機(jī)理依賴于特殊的CPU指令,比如執(zhí)行l(wèi)ock()方法的時(shí)候,cpu發(fā)出lock指令,比如我們執(zhí)行unlock的時(shí)候,cpu發(fā)出lock指令,可以認(rèn)為不受JVM的約束,并可以通過(guò)其他語(yǔ)言平臺(tái)來(lái)完成底層的實(shí)現(xiàn)。在并發(fā)量較小的多線程應(yīng)用程序中,ReentrantLock與synchronized性能相差無(wú)幾,但在高并發(fā)量的條件下,synchronized性能會(huì)迅速下降幾十倍,而ReentrantLock的性能卻能依然維持一個(gè)水準(zhǔn),高并發(fā)量情況下使用ReentrantLock。

ReentrantLock通過(guò)方法lock()與unlock()來(lái)進(jìn)行加鎖與解鎖操作,與synchronized會(huì)被JVM自動(dòng)解鎖機(jī)制不同,ReentrantLock加鎖后需要手動(dòng)進(jìn)行解鎖。為了避免程序出現(xiàn)異常而無(wú)法正常解鎖的情況,使用ReentrantLock必須在finally控制塊中進(jìn)行解鎖操作。通常使用方式如下所示

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

對(duì)于ReentrantLock而言,ReentrantLock在構(gòu)造函數(shù)中提供了是否公平鎖的初始化方式,默認(rèn)為非公平鎖。這是因?yàn)?,非公平鎖實(shí)際執(zhí)行的效率要遠(yuǎn)遠(yuǎn)超出公平鎖、ReentrantLock也是互斥鎖、也是獨(dú)享鎖。


3)Semaphore
互斥是進(jìn)程同步關(guān)系的一種特殊情況,相當(dāng)于只存在一個(gè)臨界資源,因此同時(shí)最多只能給一個(gè)線程提供服務(wù)。但是,在實(shí)際復(fù)雜的多線程應(yīng)用程序中,可能存在多個(gè)臨界資源,這時(shí)候我們可以借助Semaphore信號(hào)量來(lái)完成多個(gè)臨界資源的訪問(wèn)
,通過(guò)acquire()與release()方法來(lái)獲得和釋放臨界資源,Semaphore和ReentrantLock用法差不多,Semaphore的鎖釋放操作也由手動(dòng)進(jìn)行,因此與ReentrantLock一樣,為避免線程因拋出異常而無(wú)法正常釋放鎖的情況發(fā)生,釋放鎖的操作也必須在finally代碼塊中完成,
構(gòu)造方法里面也可以設(shè)置否公平鎖的初始化方式,默認(rèn)為非公平鎖。


4)AtomicInteger
在多線程程序中,諸如++i或i++等運(yùn)算不具有原子性,是不安全的線程操作之一。通常我們會(huì)使用synchronized將該操作變成一個(gè)原子操作,但JVM為此類(lèi)操作特意提供了一些同步類(lèi),使得使用更方便,且使程序運(yùn)行效率變得更高。通常AtomicInteger的性能是ReentantLock的好幾倍。

 
三、各個(gè)鎖的優(yōu)勢(shì)

1.synchronized:

在資源競(jìng)爭(zhēng)不是很激烈的情況,偶爾會(huì)有同步的情形下,synchronized是很合適的。原因在于,編譯程序通常會(huì)盡可能的進(jìn)行優(yōu)化synchronize,另外可讀性非常好,synchronized它是通過(guò)悲觀鎖實(shí)現(xiàn)的。


2.ReentrantLock:

在資源競(jìng)爭(zhēng)不激烈的情形下,性能稍微比synchronized差點(diǎn)點(diǎn)。但是當(dāng)同步非常激烈的時(shí)候,synchronized的性能一下子能下降好幾十倍,而ReentrantLock確還能維持常態(tài)。

高并發(fā)量情況下使用ReentrantLock。


3.Atomic:

和上面的類(lèi)似,不激烈情況下,性能比synchronized略遜,而激烈的時(shí)候,也能維持常態(tài)。激烈的時(shí)候,Atomic的性能會(huì)優(yōu)于ReentrantLock一倍左右。但是其有一個(gè)缺點(diǎn),就是只能同步一個(gè)值,一段代碼中只能出現(xiàn)一個(gè)Atomic的變量,多于一個(gè)同步無(wú)效。因?yàn)樗荒茉诙鄠€(gè)Atomic之間同步,是基于cas操作來(lái)實(shí)現(xiàn)的,它是通過(guò)樂(lè)觀鎖來(lái)實(shí)現(xiàn)的。
 

參考地址:https://youzhixueyuan.com/4-kinds-of-java-thread-locks.html  然后自己再對(duì)比比較和精簡(jiǎn)分析


4 synchronized與Lock的區(qū)別

1).首先synchronized是java內(nèi)置關(guān)鍵字,是在在jvm層面,Lock是一個(gè)接口,最后是由CPU來(lái)發(fā)送lock和unlock指令,這個(gè)和volatile底層原理實(shí)現(xiàn)類(lèi)似。

2).synchronized無(wú)法判斷是否獲取鎖的狀態(tài),Lock可以判斷是否獲取到鎖;

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

4) .用synchronized關(guān)鍵字的兩個(gè)線程1和線程2,如果當(dāng)前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會(huì)一直等待下去,而Lock鎖就不一定會(huì)等待下去,如果嘗試獲取不到鎖,線程可以不用一直等待就結(jié)束了;

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

6) .Lock鎖適合大量同步的代碼的同步問(wèn)題,synchronized鎖適合代碼少量的同步問(wèn)題。
 

 作者:chen.yu
深信服三年半工作經(jīng)驗(yàn),目前就職游戲廠商,希望能和大家交流和學(xué)習(xí),
微信公眾號(hào):編程入門(mén)到禿頭 或掃描下面二維碼
零基礎(chǔ)入門(mén)進(jìn)階人工智能(鏈接)