基于CAS的一些鎖(1)- ReentrantLock

synchronized的可重入性

如果一個(gè)同步方法m1中調(diào)用了另一個(gè)同步方法m2,并且這兩個(gè)方法加的是同一把鎖。那么在一個(gè)線程調(diào)用m1時(shí)就得到了這把鎖,m1中調(diào)m2時(shí)發(fā)現(xiàn)是同一個(gè)線程,m2也能得到這把鎖。這是鎖的可重入。

所謂可重入鎖就是拿到這把鎖之后可以再加多道鎖,但鎖定的還是同一對(duì)象,被嵌套調(diào)用的同步方法執(zhí)行完就去一道。

public class SyncTest_03 {

    public synchronized void m1() {
        System.out.println("m1 start");
        m2(); // m2的第二道鎖被釋放后才繼續(xù)往下執(zhí)行
        System.out.println("m1 end");
    }

    public synchronized void m2() {
        System.out.println("m2");
    }

    public static void main(String[] args) {
        SyncTest_03 syncTest03 = new SyncTest_03();
        new Thread(syncTest03::m1).start();

    }
}
  • 模擬子類同步方法調(diào)用父類同步方法:
    注:子類和父類是同一把鎖
public class SyncTest_04 {

    public synchronized void m() {
        System.out.println("m start");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m end");
    }

    public static void main(String[] args) {
        Child child = new Child();
        new Thread(child::m).start();
    }
}

class Child extends SyncTest_04 {
    @Override
    public synchronized void m() {
        System.out.println("child m start");
        super.m(); // 調(diào)用父類是同一把鎖
        System.out.println("child m end");
    }
}

ReentrantLock

ReentrantLock可以替代synchronized,需把原來synchronized的地方換成lock.lock(),加完鎖還需要手動(dòng)解鎖lock.unlock(),并且要在try...finally中手動(dòng)解鎖,保證程序執(zhí)行完或異常后能釋放鎖。synchronized是自動(dòng)解鎖的。

public class T01_ReentrantLock {

    Lock lock = new ReentrantLock();

    public void m1() {
        try {
            lock.lock(); // synchronized(this)
            System.out.println("m1 start");
            m2(); // m2的第二道鎖被釋放后才繼續(xù)往下執(zhí)行
            System.out.println("m1 end");
        } finally {
            lock.unlock(); // 解鎖
        }
    }

    public void m2() {
        try {
            lock.lock();
            System.out.println("m2");
        } finally {
            lock.unlock(); // 解鎖
        }
    }

    public static void main(String[] args) {
        T01_ReentrantLock instance = new T01_ReentrantLock();
        new Thread(instance::m1).start();
    }
}

既然ReentrantLock與synchronized都可重入,那ReentrantLock有什么用?

tryLock()

ReentrantLock當(dāng)然還有特別的功能,可以使用tryLock進(jìn)行嘗試鎖定,不管鎖定與否,方法都將繼續(xù)執(zhí)行。lock.tryLock()返回boolean類型。
synchronized如果鎖定失敗就阻塞了,但是用ReentrantLock可以自己決定要不要wait。

public class T01_ReentrantLock1 {

    Lock lock = new ReentrantLock();

    public void m1() {
        try {
            lock.lock(); // synchronized(this)
            System.out.println("m1 start");
            TimeUnit.SECONDS.sleep(7);
            System.out.println("m1 end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void m2() {
        boolean locked = false;
        try {
            // locked = lock.tryLock();
            locked = lock.tryLock(5, TimeUnit.SECONDS);
            System.out.println("m2..." + locked);
        }  catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (locked) {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        T01_ReentrantLock1 instance = new T01_ReentrantLock1();
        new Thread(instance::m1).start();
        new Thread(instance::m2).start();
    }
}

m1、m2 start,m1先睡7s,m2 tryLock 5s,此時(shí)鎖還沒被m1釋放,tryLock返回false。若m1睡4s,釋放鎖后m2 tryLock返回true。

lockInterruptibly()

ReentrantLock可以用 lock.lockInterruptibly(),對(duì)interrupt()方法做出響應(yīng)。

線程在請(qǐng)求lock并被阻塞時(shí),如果被interrupt,則此線程會(huì)被喚醒并被要求處理InterruptedException。并且如果線程已經(jīng)被interrupt,再使用lockInterruptibly的時(shí)候,此線程也會(huì)被要求處理interruptedException.

若此線程在運(yùn)行中沒有阻塞, 則不會(huì)處理interruptedException。但此線程的 “打斷標(biāo)志”會(huì)被設(shè)置, 可通過isInterrupted()查看并作出處理。

public class T01_ReentrantLock2 {

    Lock lock = new ReentrantLock();

    public void m1() {
        try {
            lock.lock(); // synchronized(this)
            System.out.println("m1 start");
            TimeUnit.SECONDS.sleep(10);
            System.out.println("m1 end");
        } catch (InterruptedException e) {
            System.out.println("Interrupted!!");
        } finally {
            lock.unlock();
        }
    }

    public void m2() {
        boolean locked = false;
        try {
            lock.lockInterruptibly(); // 可以對(duì)interrupt()方法做出響應(yīng)
            System.out.println("m2 start");
            TimeUnit.SECONDS.sleep(5);
            System.out.println("m2 end");
        }  catch (InterruptedException e) {
            System.out.println("m2 Interrupted!!");
        } finally {
            if (locked) {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        T01_ReentrantLock2 instance = new T01_ReentrantLock2();
        Thread t1 = new Thread(instance::m1);
        Thread t2 = new Thread(instance::m2);
        t1.start();
        t2.start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.interrupt(); // 打斷線程2的等待
    }

}
公平鎖與非公平鎖
  • 什么是公平鎖與非公平鎖?
    如果是公平鎖,一個(gè)線程會(huì)先檢查等待隊(duì)列里是否有其它線程,如果有進(jìn)入隊(duì)列排隊(duì),等別的線程先運(yùn)行;
    如果是非公平鎖,一個(gè)線程不管其它線程是否先執(zhí)行,而是上來就競(jìng)爭(zhēng)鎖。

ReentrantLock默認(rèn)是非公平鎖,但是可指定為公平鎖。

// 參數(shù)為true是公平鎖
ReentrantLock lock = new ReentrantLock(true);

總結(jié)

  • ReentrantLock可以替代synchronized,也可重入,底層是cas
  • tryLock:嘗試鎖定,不管鎖定與否,都將繼續(xù)執(zhí)行。tryLock()返回boolean類型
  • lockInterruptibly:被interrupt()后,線程會(huì)被喚醒并被要求處理InterruptedException
  • ReentrantLock默認(rèn)為非公平鎖,new ReentrantLock(true) 設(shè)置為公平鎖
最后編輯于
?著作權(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ù)。

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