可重入鎖

定義

可重入鎖,也叫做遞歸鎖,指的是在同一線程內(nèi),外層函數(shù)獲得鎖之后,內(nèi)層遞歸函數(shù)仍然可以獲取到該鎖。換一種說(shuō)法:同一個(gè)線程再次進(jìn)入同步代碼時(shí),可以使用自己已獲取到的鎖。

作用

防止在同一線程中多次獲取鎖而導(dǎo)致死鎖發(fā)生。

如下,我們通過(guò)自旋鎖來(lái)判斷是否會(huì)發(fā)生死鎖:

public class SpinLock {
    private AtomicReference<Thread> sign = new AtomicReference<>();
    public void lock(){
        Thread current = Thread.currentThread();
        while (!sign.compareAndSet(null,current)){
        }
    }
    public void unlock(){
        Thread cur = Thread.currentThread();
        sign.compareAndSet(cur,null);
    }
}

public class SpinLockDemo {
    private SpinLock lock =  new SpinLock();
    class Widget{
        public void doSomething(){
            lock.lock();
            System.out.println("Widget calling doSomething");
            lock.unlock();
        }
    }

    class LoggingWidget extends Widget {
        @Override
        public void doSomething() {
            lock.lock();
            System.out.println("LoggingWidget calling doSomething");
            super.doSomething();
            lock.unlock();
        }
    }

    public static void main(String[] args){
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        SpinLockDemo.Widget widget = spinLockDemo.new LoggingWidget();
        widget.doSomething();
    }
}

輸出結(jié)果:
LoggingWidget calling doSomething

我們可以看到在LoggingWidget類中doSomething方法時(shí),通過(guò)鎖進(jìn)入臨界區(qū),并在臨界區(qū)中調(diào)用了父類的該方法,而父類的方法要獲取到同一個(gè)鎖,被阻塞,導(dǎo)致死鎖發(fā)生。

常見的可重入鎖

  • Synchronized關(guān)鍵字:
public class SynchronizedDemo {
     class Widget{
        public synchronized void doSomething(){
            System.out.println("Widget calling doSomething...");
        }
    }

     class LoggingWidget extends Widget{
        @Override
        public synchronized void doSomething() {
            System.out.println("LoggingWidget calling doSomething");
            super.doSomething();
        }
    }

    public static void main(String[] args){
         SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
        SynchronizedDemo.Widget widget = synchronizedDemo.new LoggingWidget();
        widget.doSomething();
    }
}

輸出結(jié)果:
LoggingWidget calling doSomething
Widget calling doSomething...

根據(jù)結(jié)果,我們可以看到Synchronized關(guān)鍵字是可重入鎖。

  • ReetrantLock:
public class ReentrantLockDemo {
    private Lock lock =  new ReentrantLock();
    class Widget{
        public void doSomething(){
            lock.lock();
            System.out.println("Widget calling doSomething");
            lock.unlock();
        }
    }

    class LoggingWidget extends Widget {
        @Override
        public void doSomething() {
            lock.lock();
            System.out.println("LoggingWidget calling doSomething");
            super.doSomething();
            lock.unlock();
        }
    }

    public static void main(String[] args){
        ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
        Widget widget = reentrantLockDemo.new LoggingWidget();
        widget.doSomething();
    }

}

輸出結(jié)果:
LoggingWidget calling doSomething
Widget calling doSomething

根據(jù)結(jié)果,我們可以看出ReetrantLock鎖時(shí)可重入的。

實(shí)現(xiàn)可重入鎖

為每個(gè)鎖關(guān)聯(lián)一個(gè)獲取計(jì)數(shù)器和一個(gè)所有者線程,當(dāng)計(jì)數(shù)值為0時(shí),這個(gè)鎖就被認(rèn)為是沒(méi)有被任何線程所占有的。當(dāng)線程請(qǐng)求一個(gè)未被持有的鎖時(shí),計(jì)數(shù)值將會(huì)遞增。而當(dāng)線程退出同步代碼時(shí),計(jì)數(shù)器會(huì)相應(yīng)地遞減。當(dāng)計(jì)數(shù)值為0時(shí),則釋放該鎖。

如下:我們通過(guò)修改自旋鎖來(lái)實(shí)現(xiàn)一個(gè)可重入的自旋鎖

public class SpinLockDemo {
    private MySpinLock lock =  new MySpinLock();
    class Widget{
        public void doSomething(){
            lock.lock();
            System.out.println("Widget calling doSomething");
            lock.unlock();
        }
    }

    class MySpinLock{
        private AtomicReference<Thread> owner = new AtomicReference<>();
        private int count = 0;
        public void lock(){
            Thread cur = Thread.currentThread();
            if (cur == owner.get()){
                count ++;
                return;
            }
            while (! owner.compareAndSet(null,cur)){

            }
        }

        public void unlock(){
            Thread cur = Thread.currentThread();
            if (cur == owner.get()){
                if (count != 0){
                    count --;
                } else {
                    owner.compareAndSet(cur,null);
                }
            }
        }
    }

    class LoggingWidget extends Widget {
        @Override
        public void doSomething() {
            lock.lock();
            System.out.println("LoggingWidget calling doSomething");
            super.doSomething();
            lock.unlock();
        }
    }

    public static void main(String[] args){
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        SpinLockDemo.Widget widget = spinLockDemo.new LoggingWidget();
        widget.doSomething();
    }
}

輸出結(jié)果:
LoggingWidget calling doSomething
Widget calling doSomething
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 作者: 一字馬胡 轉(zhuǎn)載標(biāo)志 【2017-11-03】 更新日志 前言 在java中,鎖是實(shí)現(xiàn)并發(fā)的關(guān)鍵組件,多個(gè)...
    一字馬胡閱讀 44,319評(píng)論 1 32
  • 場(chǎng)景:進(jìn)屋排隊(duì)打印東西 可重入鎖:排隊(duì)拿到打印權(quán)(鑰匙),開門開鎖,打印完畢,關(guān)門,打算交鑰匙,同學(xué)打電話給來(lái),幫...
    青城樓主閱讀 1,672評(píng)論 0 1
  • 上一篇文章中講述了信號(hào)量和互斥量,其中互斥量一般用于保證對(duì)于資源的互斥訪問(wèn),和鎖的本質(zhì)一樣。本文講述簡(jiǎn)單鎖的實(shí)現(xiàn)和...
    anytimekaka閱讀 21,718評(píng)論 13 36
  • Lock鎖接口在JAVA SE5之后,出現(xiàn)在并發(fā)包中.它提供了與synchronized關(guān)鍵字一樣的同步功能.只是...
    薛云龍閱讀 2,791評(píng)論 0 0
  • 跟著網(wǎng)上的小視頻畫的小怪獸們,哈哈,有的還是挺丑的,那個(gè)像螃蟹一樣的覺得蠻好看的。
    紫看片子閱讀 175評(píng)論 1 1

友情鏈接更多精彩內(nèi)容