<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          到底什么是重入鎖?拜托,一次搞清楚!

          共 463字,需瀏覽 1分鐘

           ·

          2021-12-01 09:53

          JDK中獨(dú)占鎖的實(shí)現(xiàn)除了使用關(guān)鍵字synchronized外,還可以使用ReentrantLock雖然在性能上ReentrantLock和synchronized沒(méi)有什么區(qū)別,但ReentrantLock相比synchronized而言功能更加豐富,使用起來(lái)更為靈活,也更適合復(fù)雜的并發(fā)場(chǎng)景。

          兩者的相同點(diǎn)

          ReentrantLock是獨(dú)占鎖且可重入的

          例子

          public?class?ReentrantLockTest?{

          ????public?static?void?main(String[]?args)?throws?InterruptedException?{

          ????????ReentrantLock?lock?=?new?ReentrantLock();

          ????????for?(int?i?=?1;?i?<=?3;?i++)?{
          ????????????lock.lock();
          ????????}

          ????????for(int?i=1;i<=3;i++){
          ????????????try?{

          ????????????}?finally?{
          ????????????????lock.unlock();
          ????????????}
          ????????}
          ????}
          }

          上面的代碼通過(guò)lock()方法先獲取鎖三次,然后通過(guò)unlock()方法釋放鎖3次,程序可以正常退出。從上面的例子可以看出,ReentrantLock是可以重入的鎖,當(dāng)一個(gè)線(xiàn)程獲取鎖時(shí),還可以接著重復(fù)獲取多次。在加上ReentrantLock的的獨(dú)占性,我們可以得出以下ReentrantLocksynchronized的相同點(diǎn)。

          • ReentrantLocksynchronized都是獨(dú)占鎖,只允許線(xiàn)程互斥的訪(fǎng)問(wèn)臨界區(qū)。但是實(shí)現(xiàn)上兩者不同:synchronized加鎖解鎖的過(guò)程是隱式的,用戶(hù)不用手動(dòng)操作,優(yōu)點(diǎn)是操作簡(jiǎn)單,但顯得不夠靈活。一般并發(fā)場(chǎng)景使用synchronized的就夠了;ReentrantLock需要手動(dòng)加鎖和解鎖,且解鎖的操作盡量要放在finally代碼塊中,保證線(xiàn)程正確釋放鎖。ReentrantLock操作較為復(fù)雜,但是因?yàn)榭梢允謩?dòng)控制加鎖和解鎖過(guò)程,在復(fù)雜的并發(fā)場(chǎng)景中能派上用場(chǎng)。
          • ReentrantLocksynchronized都是可重入的。synchronized因?yàn)榭芍厝胍虼丝梢苑旁诒贿f歸執(zhí)行的方法上,且不用擔(dān)心線(xiàn)程最后能否正確釋放鎖;而ReentrantLock在重入時(shí)要確保保重復(fù)獲取鎖的次數(shù)必須和重復(fù)釋放鎖的次數(shù)一樣,否則可能導(dǎo)致其他線(xiàn)程無(wú)法獲得該鎖。

          兩者的額外功能

          ReentrantLock可以實(shí)現(xiàn)公平鎖

          公平鎖是指當(dāng)鎖可用時(shí),在鎖上等待時(shí)間最長(zhǎng)的線(xiàn)程將獲得鎖的使用權(quán)。而非公平鎖則隨機(jī)分配這種使用權(quán)。和synchronized一樣,默認(rèn)的ReentrantLock實(shí)現(xiàn)是非公平鎖,因?yàn)橄啾裙芥i,非公平鎖性能更好。當(dāng)然公平鎖能防止饑餓,某些情況下也很有用。在創(chuàng)建ReentrantLock的時(shí)候通過(guò)傳進(jìn)參數(shù)true創(chuàng)建公平鎖,如果傳入的是false或沒(méi)傳參數(shù)則創(chuàng)建的是非公平鎖

          ReentrantLock?lock?=?new?ReentrantLock(true);

          繼續(xù)跟進(jìn)看下源碼

          /**
          ?*?Creates?an?instance?of?{@code?ReentrantLock}?with?the
          ?*?given?fairness?policy.
          ?*
          ?*?@param?fair?{@code?true}?if?this?lock?should?use?a?fair?ordering?policy
          ?*/
          public?ReentrantLock(boolean?fair)?{
          ????sync?=?fair???new?FairSync()?:?new?NonfairSync();
          }

          可以看到公平鎖和非公平鎖的實(shí)現(xiàn)關(guān)鍵在于成員變量sync的實(shí)現(xiàn)不同,這是鎖實(shí)現(xiàn)互斥同步的核心。以后有機(jī)會(huì)我們?cè)偌?xì)講。

          一個(gè)公平鎖的例子

          public?class?ReentrantLockTest?{

          ????static?Lock?lock?=?new?ReentrantLock(true);

          ????public?static?void?main(String[]?args)?throws?InterruptedException?{

          ????????for(int?i=0;i<5;i++){
          ????????????new?Thread(new?ThreadDemo(i)).start();
          ????????}

          ????}

          ????static?class?ThreadDemo?implements?Runnable?{
          ????????Integer?id;

          ????????public?ThreadDemo(Integer?id)?{
          ????????????this.id?=?id;
          ????????}

          ????????@Override

          ??????public?void?run()?{
          ????????????try?{
          ????????????????TimeUnit.MILLISECONDS.sleep(10);
          ????????????}?catch?(InterruptedException?e)?{
          ????????????????e.printStackTrace();
          ????????????}
          ????????????for(int?i=0;i<2;i++){
          ????????????????lock.lock();
          ????????????????System.out.println("獲得鎖的線(xiàn)程:"+id);
          ????????????????lock.unlock();
          ????????????}
          ????????}
          ????}
          }

          公平鎖結(jié)果

          我們開(kāi)啟5個(gè)線(xiàn)程,讓每個(gè)線(xiàn)程都獲取釋放鎖兩次。為了能更好的觀察到結(jié)果,在每次獲取鎖前讓線(xiàn)程休眠10毫秒??梢钥吹骄€(xiàn)程幾乎是輪流的獲取到了鎖。如果我們改成非公平鎖,再看下結(jié)果

          非公平鎖結(jié)果

          線(xiàn)程會(huì)重復(fù)獲取鎖。如果申請(qǐng)獲取鎖的線(xiàn)程足夠多,那么可能會(huì)造成某些線(xiàn)程長(zhǎng)時(shí)間得不到鎖。這就是非公平鎖的“饑餓”問(wèn)題。

          公平鎖和非公平鎖該如何選擇?

          大部分情況下我們使用非公平鎖,因?yàn)槠湫阅鼙裙芥i好很多。

          但是公平鎖能夠避免線(xiàn)程饑餓,某些情況下也很有用。

          ReentrantLock可響應(yīng)中斷

          當(dāng)使用synchronized實(shí)現(xiàn)鎖時(shí),阻塞在鎖上的線(xiàn)程除非獲得鎖否則將一直等待下去,也就是說(shuō)這種無(wú)限等待獲取鎖的行為無(wú)法被中斷。而ReentrantLock給我們提供了一個(gè)可以響應(yīng)中斷的獲取鎖的方法lockInterruptibly()。該方法可以用來(lái)解決死鎖問(wèn)題。

          響應(yīng)中斷的例子:

          public?class?ReentrantLockTest?{
          ????static?Lock?lock1?=?new?ReentrantLock();
          ????static?Lock?lock2?=?new?ReentrantLock();
          ????public?static?void?main(String[]?args)?throws?InterruptedException?{

          ????????Thread?thread?=?new?Thread(new?ThreadDemo(lock1,?lock2));//該線(xiàn)程先獲取鎖1,再獲取鎖2
          ????????Thread?thread1?=?new?Thread(new?ThreadDemo(lock2,?lock1));//該線(xiàn)程先獲取鎖2,再獲取鎖1
          ????????thread.start();
          ????????thread1.start();
          ????????thread.interrupt();//是第一個(gè)線(xiàn)程中斷
          ????}

          ????static?class?ThreadDemo?implements?Runnable?{
          ????????Lock?firstLock;
          ????????Lock?secondLock;
          ????????public?ThreadDemo(Lock?firstLock,?Lock?secondLock)?{
          ????????????this.firstLock?=?firstLock;
          ????????????this.secondLock?=?secondLock;
          ????????}
          ????????@Override
          ????????public?void?run()?{
          ????????????try?{
          ????????????????firstLock.lockInterruptibly();
          ????????????????TimeUnit.MILLISECONDS.sleep(10);//更好的觸發(fā)死鎖
          ????????????????secondLock.lockInterruptibly();
          ????????????}?catch?(InterruptedException?e)?{
          ????????????????e.printStackTrace();
          ????????????}?finally?{
          ????????????????firstLock.unlock();
          ????????????????secondLock.unlock();
          ????????????????System.out.println(Thread.currentThread().getName()+"正常結(jié)束!");
          ????????????}
          ????????}
          ????}
          }

          結(jié)果:

          構(gòu)造死鎖場(chǎng)景:

          創(chuàng)建兩個(gè)子線(xiàn)程,子線(xiàn)程在運(yùn)行時(shí)會(huì)分別嘗試獲取兩把鎖。其中一個(gè)線(xiàn)程先獲取鎖1在獲取鎖2,另一個(gè)線(xiàn)程正好相反。

          如果沒(méi)有外界中斷,該程序?qū)⑻幱谒梨i狀態(tài)永遠(yuǎn)無(wú)法停止。我們通過(guò)使其中一個(gè)線(xiàn)程中斷,來(lái)結(jié)束線(xiàn)程間毫無(wú)意義的等待。被中斷的線(xiàn)程將拋出異常,而另一個(gè)線(xiàn)程將能獲取鎖后正常結(jié)束。

          3.3 獲取鎖時(shí)限時(shí)等待

          ReentrantLock還給我們提供了獲取鎖限時(shí)等待的方法tryLock(),可以選擇傳入時(shí)間參數(shù),表示等待指定的時(shí)間,無(wú)參則表示立即返回鎖申請(qǐng)的結(jié)果:true表示獲取鎖成功,false表示獲取鎖失敗。我們可以使用該方法配合失敗重試機(jī)制來(lái)更好的解決死鎖問(wèn)題。

          更好的解決死鎖的例子:

          public?class?ReentrantLockTest?{
          ????static?Lock?lock1?=?new?ReentrantLock();
          ????static?Lock?lock2?=?new?ReentrantLock();
          ????public?static?void?main(String[]?args)?throws?InterruptedException?{

          ????????Thread?thread?=?new?Thread(new?ThreadDemo(lock1,?lock2));//該線(xiàn)程先獲取鎖1,再獲取鎖2
          ????????Thread?thread1?=?new?Thread(new?ThreadDemo(lock2,?lock1));//該線(xiàn)程先獲取鎖2,再獲取鎖1
          ????????thread.start();
          ????????thread1.start();
          ????}

          ????static?class?ThreadDemo?implements?Runnable?{
          ????????Lock?firstLock;
          ????????Lock?secondLock;
          ????????public?ThreadDemo(Lock?firstLock,?Lock?secondLock)?{
          ????????????this.firstLock?=?firstLock;
          ????????????this.secondLock?=?secondLock;
          ????????}
          ????????@Override
          ????????public?void?run()?{
          ????????????try?{
          ????????????????while(!lock1.tryLock()){
          ????????????????????TimeUnit.MILLISECONDS.sleep(10);
          ????????????????}
          ????????????????while(!lock2.tryLock()){
          ????????????????????lock1.unlock();
          ????????????????????TimeUnit.MILLISECONDS.sleep(10);
          ????????????????}
          ????????????}?catch?(InterruptedException?e)?{
          ????????????????e.printStackTrace();
          ????????????}?finally?{
          ????????????????firstLock.unlock();
          ????????????????secondLock.unlock();
          ????????????????System.out.println(Thread.currentThread().getName()+"正常結(jié)束!");
          ????????????}
          ????????}
          ????}
          }

          結(jié)果:

          線(xiàn)程通過(guò)調(diào)用tryLock()方法獲取鎖,第一次獲取鎖失敗時(shí)會(huì)休眠10毫秒,然后重新獲取,直到獲取成功。第二次獲取失敗時(shí),首先會(huì)釋放第一把鎖,再休眠10毫秒,然后重試直到成功為止。線(xiàn)程獲取第二把鎖失敗時(shí)將會(huì)釋放第一把鎖,這是解決死鎖問(wèn)題的關(guān)鍵,避免了兩個(gè)線(xiàn)程分別持有一把鎖然后相互請(qǐng)求另一把鎖。

          結(jié)合Condition實(shí)現(xiàn)等待通知機(jī)制

          使用synchronized結(jié)合Object上的wait和notify方法可以實(shí)現(xiàn)線(xiàn)程間的等待通知機(jī)制。ReentrantLock結(jié)合Condition接口同樣可以實(shí)現(xiàn)這個(gè)功能。而且相比前者使用起來(lái)更清晰也更簡(jiǎn)單。

          Condition使用簡(jiǎn)介

          Condition由ReentrantLock對(duì)象創(chuàng)建,并且可以同時(shí)創(chuàng)建多個(gè)

          static?Condition?notEmpty?=?lock.newCondition();

          static?Condition?notFull?=?lock.newCondition();

          Condition接口在使用前必須先調(diào)用ReentrantLock的lock()方法獲得鎖。之后調(diào)用Condition接口的await()將釋放鎖,并且在該Condition上等待,直到有其他線(xiàn)程調(diào)用Condition的signal()方法喚醒線(xiàn)程。使用方式和wait,notify類(lèi)似。

          一個(gè)使用condition的簡(jiǎn)單例子:

          public?class?ConditionTest?{

          ????static?ReentrantLock?lock?=?new?ReentrantLock();
          ????static?Condition?condition?=?lock.newCondition();
          ????public?static?void?main(String[]?args)?throws?InterruptedException?{

          ????????lock.lock();
          ????????new?Thread(new?SignalThread()).start();
          ????????System.out.println("主線(xiàn)程等待通知");
          ????????try?{
          ????????????condition.await();
          ????????}?finally?{
          ????????????lock.unlock();
          ????????}
          ????????System.out.println("主線(xiàn)程恢復(fù)運(yùn)行");
          ????}
          ????static?class?SignalThread?implements?Runnable?{

          ????????@Override
          ????????public?void?run()?{
          ????????????lock.lock();
          ????????????try?{
          ????????????????condition.signal();
          ????????????????System.out.println("子線(xiàn)程通知");
          ????????????}?finally?{
          ????????????????lock.unlock();
          ????????????}
          ????????}
          ????}
          }

          運(yùn)行結(jié)果:

          使用Condition實(shí)現(xiàn)簡(jiǎn)單的阻塞隊(duì)列

          阻塞隊(duì)列是一種特殊的先進(jìn)先出隊(duì)列,它有以下幾個(gè)特點(diǎn) 1.入隊(duì)和出隊(duì)線(xiàn)程安全 2.當(dāng)隊(duì)列滿(mǎn)時(shí),入隊(duì)線(xiàn)程會(huì)被阻塞;當(dāng)隊(duì)列為空時(shí),出隊(duì)線(xiàn)程會(huì)被阻塞。

          阻塞隊(duì)列的簡(jiǎn)單實(shí)現(xiàn):

          public?class?MyBlockingQueue<E>?{

          ????int?size;//阻塞隊(duì)列最大容量

          ????ReentrantLock?lock?=?new?ReentrantLock();

          ????LinkedList?list=new?LinkedList<>();//隊(duì)列底層實(shí)現(xiàn)

          ????Condition?notFull?=?lock.newCondition();//隊(duì)列滿(mǎn)時(shí)的等待條件
          ????Condition?notEmpty?=?lock.newCondition();//隊(duì)列空時(shí)的等待條件

          ????public?MyBlockingQueue(int?size)?{
          ????????this.size?=?size;
          ????}

          ????public?void?enqueue(E?e)?throws?InterruptedException?{
          ????????lock.lock();
          ????????try?{
          ????????????while?(list.size()?==size)//隊(duì)列已滿(mǎn),在notFull條件上等待
          ????????????????notFull.await();
          ????????????list.add(e);//入隊(duì):加入鏈表末尾
          ????????????System.out.println("入隊(duì):"?+e);
          ????????????notEmpty.signal();?//通知在notEmpty條件上等待的線(xiàn)程
          ????????}?finally?{
          ????????????lock.unlock();
          ????????}
          ????}

          ????public?E?dequeue()?throws?InterruptedException?{
          ????????E?e;
          ????????lock.lock();
          ????????try?{
          ????????????while?(list.size()?==?0)//隊(duì)列為空,在notEmpty條件上等待
          ????????????????notEmpty.await();
          ????????????e?=?list.removeFirst();//出隊(duì):移除鏈表首元素
          ????????????System.out.println("出隊(duì):"+e);
          ????????????notFull.signal();//通知在notFull條件上等待的線(xiàn)程
          ????????????return?e;
          ????????}?finally?{
          ????????????lock.unlock();
          ????????}
          ????}
          }

          測(cè)試代碼

          public?static?void?main(String[]?args)?throws?InterruptedException?{

          ????MyBlockingQueue?queue?=?new?MyBlockingQueue<>(2);
          ????for?(int?i?=?0;?i?10;?i++)?{
          ????????int?data?=?i;
          ????????new?Thread(new?Runnable()?{
          ????????????@Override
          ????????????public?void?run()?{
          ????????????????try?{
          ????????????????????queue.enqueue(data);
          ????????????????}?catch?(InterruptedException?e)?{

          ????????????????}
          ????????????}
          ????????}).start();

          ????}
          ????for(int?i=0;i<10;i++){
          ????????new?Thread(new?Runnable()?{
          ????????????@Override
          ????????????public?void?run()?{
          ????????????????try?{
          ????????????????????Integer?data?=?queue.dequeue();
          ????????????????}?catch?(InterruptedException?e)?{
          ????????????????????e.printStackTrace();
          ????????????????}
          ????????????}
          ????????}).start();
          ????}

          }

          運(yùn)行結(jié)果:

          總結(jié)

          ReentrantLock是可重入的獨(dú)占鎖。比起synchronized功能更加豐富,支持公平鎖實(shí)現(xiàn),支持中斷響應(yīng)以及限時(shí)等待等等。可以配合一個(gè)或多個(gè)Condition條件方便的實(shí)現(xiàn)等待通知機(jī)制。

          好了,今天就分享這么多。

          參考:www.cnblogs.com/takumicx

          瀏覽 52
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色就是欧美 | 天天艹天天操 | 午夜欧美成人三级 | 成人欧美18 | 亚洲精品视频在线观看免费 |