Node.js redis client用什么? 需要使用redis連接池么?

底層實(shí)現(xiàn)

synchronized底層實(shí)現(xiàn)

詳細(xì)參考:楊曉峰極客時(shí)間上的課程《Java核心技術(shù)面試精講》:第16講 | synchronized底層如何實(shí)現(xiàn)?什么是鎖的升級(jí)、降級(jí)

  • synchronized 代碼塊是由一對(duì)兒 monitorenter/monitorexit 指令實(shí)現(xiàn)的,Monitor 對(duì)象是同步的基本實(shí)現(xiàn)單元。

  • 發(fā)展歷程:

    • JDK6之前,Monitor 的實(shí)現(xiàn)完全是依靠操作系統(tǒng)內(nèi)部的互斥鎖,因?yàn)樾枰M(jìn)行用戶態(tài)到內(nèi)核態(tài)的切換,所以同步操作是一個(gè)無(wú)差別的重量級(jí)操作。(@所以說(shuō)效率低下嘛)
    • 現(xiàn)代的(Oracle)JDK 中,JVM 對(duì)此進(jìn)行了大刀闊斧地改進(jìn),提供了三種不同的 Monitor 實(shí)現(xiàn),也就是常說(shuō)的三種不同的鎖:偏斜鎖(Biased Locking)、輕量級(jí)鎖和重量級(jí)鎖大大改進(jìn)了其性能。
  • 偏斜鎖:JVM 會(huì)利用 CAS 操作(compare and swap),在對(duì)象頭上的 Mark Word 部分設(shè)置線程 ID,以表示這個(gè)對(duì)象偏向于當(dāng)前線程,所以并不涉及真正的互斥鎖

  • 輕量級(jí)鎖:

    • 如果有另外的線程試圖鎖定某個(gè)已經(jīng)被偏斜過(guò)的對(duì)象,JVM 就需要撤銷(xiāo)(revoke)偏斜鎖,并切換到輕量級(jí)鎖實(shí)現(xiàn)。輕量級(jí)鎖依賴(lài) CAS 操作 Mark Word 來(lái)試圖獲取鎖,如果重試成功,就使用普通的輕量級(jí)鎖;否則,進(jìn)一步升級(jí)為重量級(jí)鎖。
    • 因?yàn)橹亓考?jí)鎖性能差,所以輕量級(jí)鎖又衍生出了一種鎖:自旋鎖,其實(shí)現(xiàn)就是自循環(huán)若干次,通過(guò)CAS操作MARK WROD試圖獲取鎖

其他鎖模型

詳細(xì)參考:王寶令極客時(shí)間上的課程《Java并發(fā)編程實(shí)戰(zhàn)》- 08 | 管程:并發(fā)編程的萬(wàn)能鑰匙

  • 管程模型:
    • Hasen模型:要求 notify() 放在代碼的最后,這樣 T2 通知完 T1 后,T2 就結(jié)束了,然后 T1 再執(zhí)行,這樣就能保證同一時(shí)刻只有一個(gè)線程執(zhí)行。
    • Hoare模型:T2 通知完 T1 后,T2 阻塞,T1 馬上執(zhí)行;等 T1 執(zhí)行完,再喚醒 T2,也能保證同一時(shí)刻只有一個(gè)線程執(zhí)行。但是相比 Hasen 模型,T2 多了一次阻塞喚醒操作。
    • MESA模型(JAVA參考實(shí)現(xiàn)):MESA 管程里面,T2 通知完 T1 后,T2 還是會(huì)接著執(zhí)行,T1 并不立即執(zhí)行,僅僅是從條件變量的等待隊(duì)列進(jìn)到入口等待隊(duì)列里面。這樣做的好處是 notify() 不用放到代碼的最后,T2 也沒(méi)有多余的阻塞喚醒操作。但是也有個(gè)副作用,就是當(dāng) T1 再次執(zhí)行的時(shí)候,可能曾經(jīng)滿足的條件,現(xiàn)在已經(jīng)不滿足了,所以需要以循環(huán)方式檢驗(yàn)條件變量?!簿褪钱a(chǎn)生假喚醒

鎖變化

升級(jí)/膨脹

其實(shí)就是偏斜鎖=》輕量級(jí)鎖=》重量級(jí)鎖的過(guò)程,見(jiàn)第一節(jié) #底層實(shí)現(xiàn)

鎖降級(jí)

鎖降級(jí)確實(shí)是會(huì)發(fā)生的,當(dāng) JVM 進(jìn)入安全點(diǎn)(SafePoint)的時(shí)候,會(huì)檢查是否有閑置的 Monitor,然后試圖進(jìn)行降級(jí)。

synchronized鎖的范圍

  • 范圍
    • 代碼塊
    • 方法
    • 對(duì)象
    • 類(lèi)
  • 對(duì)象鎖和類(lèi)
    • 類(lèi)鎖和對(duì)象鎖是分開(kāi)的,(現(xiàn)在只是個(gè)概念,用來(lái)區(qū)分對(duì)象鎖的,是指靜態(tài)方法的鎖),程序中獲得類(lèi)鎖的同時(shí)也可以獲得對(duì)象鎖。
    • 同一個(gè)類(lèi)鎖和同一個(gè)類(lèi)鎖是互斥的,同一個(gè)對(duì)象鎖和同一個(gè)對(duì)象鎖互斥。 非靜態(tài)方法不受類(lèi)鎖的影響
    • 對(duì)象鎖與實(shí)例對(duì)象相關(guān), 不同的對(duì)象的對(duì)象鎖不一樣,可以同時(shí)獲取兩個(gè)不同對(duì)象的對(duì)象鎖
package com.keven;

//類(lèi)鎖和對(duì)象鎖的測(cè)試代碼
public class SyncTest {

    public static void main(String[] args) throws Exception {
        runObjectLockTest();
        System.out.println("finished runObjectLockTest");
        runClassLockTest();
        System.out.println("finished runClassLockTest");
        runClassObjectLockTest();
        System.out.println("finished runClassObjectLockTest");
        Thread.sleep(10000);
    }

    //測(cè)試對(duì)象鎖和類(lèi)鎖是否能夠同時(shí)獲取, 可以看到兩個(gè)線程打印數(shù)據(jù)不受影響,說(shuō)明不是同一個(gè)鎖
    private static void runClassObjectLockTest() {
        new Thread(SyncTest::testClassLock1, "thread1").start();
        new Thread(() -> {
            new SyncTest().testObjectLock();
        }, "thread2").start();
    }

    //測(cè)試類(lèi)鎖,顯示thread1打印完成,后面thread2才開(kāi)始打印,從側(cè)面驗(yàn)證獲取到的是同一個(gè)鎖
    private static void runClassLockTest() {
        new Thread(SyncTest::testClassLock1, "thread1").start();
        new Thread(SyncTest::testClassLock2, "thread2").start();

    }

    //測(cè)試對(duì)象鎖, 可以看到兩個(gè)線程打印數(shù)據(jù)不受影響, 且this對(duì)象的hash值不一樣
    private static void runObjectLockTest() {
        final SyncTest syncTest = new SyncTest();
        final Thread thread1 = new Thread(() -> {
            syncTest.testObjectLock();
        }, "thread1");

        final Thread thread2 = new Thread(() -> {
            new SyncTest().testObjectLock();
        }, "thread2");
        thread1.start();
        thread2.start();
    }

    private static synchronized void testClassLock1() {
        int i = 100;
        int count = 0;
        while ((i-- > 0) && (count++ < 10)) {
            System.out.println("method testClassLock1--" + Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(50);
            } catch (InterruptedException ie) {
            }
        }
    }

    private static synchronized void testClassLock2() {
        int i = 100;
        int count = 0;
        while ((i-- > 0) && (count++ < 10)) {
            System.out.println("method testClassLock2--" + Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(50);
            } catch (InterruptedException ie) {
            }
        }
    }

    private void testObjectLock() {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + " : " + this);
            int i = 100;
            int count = 0;
            while ((i-- > 0) && (count++ < 5)) {
                System.out.println("method testObjectLock--" + Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException ie) {
                }
            }
        }
    }

}

synchronized和ReentrantLock有什么區(qū)別?

詳細(xì)可參考:楊曉峰極客時(shí)間上的課程《Java核心技術(shù)面試精講》:第15講 | synchronized和ReentrantLock有什么區(qū)別呢?

  • synchronized 和 ReentrantLock 的性能不能一概而論:
    • 早起版本的synchronize在很多場(chǎng)景下性能相差較大
    • 在后續(xù)版本進(jìn)行了較多的改進(jìn),在低競(jìng)爭(zhēng)場(chǎng)景中表現(xiàn)可能由于ReentrantLock
  • 這里所謂的公平性是指在競(jìng)爭(zhēng)場(chǎng)景中,當(dāng)公平性為真時(shí),會(huì)傾向于將鎖賦予等待時(shí)間最久的線程。公平性是減少線程“饑餓”(個(gè)別線程長(zhǎng)期等待鎖,但始終無(wú)法獲取)情況發(fā)生的一個(gè)辦法。
  • ReentrantLock與Synchronized的區(qū)別:
    • ReentrantLock
      • 更加的靈活,但必須手動(dòng)釋放鎖
        • 可通過(guò)條件控制同步
        • 可被中斷,并拋出中斷異常,釋放鎖
        • 可選擇獲取鎖的超時(shí)時(shí)間,嘗試獲取鎖
        • 可選擇是否為公平鎖
      • 只適合代碼塊的鎖
    • synchronized
      • 無(wú)需釋放鎖,自動(dòng)處理
      • 可修飾方法,類(lèi),代碼塊
      • 非公平鎖,如果阻塞則必須等待cpu調(diào)度
  • ReentrantLock與Synchronized的共通點(diǎn):都是獨(dú)占鎖或者說(shuō)是排它鎖

關(guān)聯(lián)關(guān)鍵詞

  • 在上面的代碼中,我用的是 notifyAll() 來(lái)實(shí)現(xiàn)通知機(jī)制,為什么不使用 notify() 呢?
    • 這二者是有區(qū)別的,notify() 是會(huì)隨機(jī)地通知等待隊(duì)列中的一個(gè)線程,而 notifyAll() 會(huì)通知等待隊(duì)列中的所有線程。
    • 從感覺(jué)上來(lái)講,應(yīng)該是 notify() 更好一些,因?yàn)榧幢阃ㄖ芯€程,也只有一個(gè)線程能夠進(jìn)入臨界區(qū)。但那所謂的感覺(jué)往往都蘊(yùn)藏著風(fēng)險(xiǎn),實(shí)際上使用 notify() 也很有風(fēng)險(xiǎn),它的風(fēng)險(xiǎn)在于可能導(dǎo)致某些線程永遠(yuǎn)不會(huì)被通知到。@隨機(jī)的弊病不就是存在永遠(yuǎn)不被輪到的弊病么?這跟非公平鎖的弊病是一個(gè)意思
  • wait與sleep區(qū)別在于:
    • wait會(huì)釋放所有鎖而sleep不會(huì)釋放鎖資源.
    • wait只能在同步方法和同步塊中使用,而sleep任何地方都可以
    • wait無(wú)需捕捉異常,而sleep需要
    • sleep是Thread的方法,而wait是Object類(lèi)的方法;
    • sleep方法調(diào)用的時(shí)候必須指定時(shí)間
      兩者相同點(diǎn):都會(huì)讓渡CPU執(zhí)行時(shí)間,等待再次調(diào)度!。補(bǔ)充關(guān)于二者的區(qū)別還可以看知乎的這篇帖子
  • wait()方法與sleep()方法的不同之處在于,wait()方法會(huì)釋放對(duì)象的“鎖標(biāo)志”。當(dāng)調(diào)用某一對(duì)象的wait()方法后,會(huì)使當(dāng)前線程暫停執(zhí)行,并將當(dāng)前線程放入對(duì)象等待池中,直到調(diào)用了notify()方法后,將從對(duì)象等待池中移出任意一個(gè)線程并放入鎖標(biāo)志等待池中,只有鎖標(biāo)志等待池中的線程可以獲取鎖標(biāo)志,它們隨時(shí)準(zhǔn)備爭(zhēng)奪鎖的擁有權(quán)。當(dāng)調(diào)用了某個(gè)對(duì)象的notifyAll()方法,會(huì)將對(duì)象等待池中的所有線程都移動(dòng)到該對(duì)象的鎖標(biāo)志等待池。
  • sleep()方法需要指定等待的時(shí)間,它可以讓當(dāng)前正在執(zhí)行的線程在指定的時(shí)間內(nèi)暫停執(zhí)行,進(jìn)入阻塞狀態(tài),該方法既可以讓其他同優(yōu)先級(jí)或者高優(yōu)先級(jí)的線程得到執(zhí)行的機(jī)會(huì),也可以讓低優(yōu)先級(jí)的線程得到執(zhí)行機(jī)會(huì)。但是sleep()方法不會(huì)釋放“鎖標(biāo)志”,也就是說(shuō)如果有synchronized同步塊,其他線程仍然不能訪問(wèn)共享數(shù)據(jù)

常見(jiàn)面試題

  • synchronized和ReentrantLock的區(qū)別 @見(jiàn)筆記
  • 鎖什么時(shí)候升級(jí)/降級(jí)?@見(jiàn)筆記
  • 類(lèi)鎖和對(duì)象鎖的區(qū)別? @見(jiàn)筆記
  • 為什么JDK8中ConcurrentHashMap的鎖實(shí)現(xiàn)要用CAS+synchronized來(lái)取代Segment+ReentrantLock呢?
  • 為什么wait必須是在同步塊中的呢?@重看了一遍王寶令的課程,發(fā)現(xiàn)這是MESA管程模型的設(shè)計(jì)范式,硬要解釋的話可以是這樣:
    • wait是跟notify, notifyAll配對(duì)的, 是和synchronized關(guān)鍵字一起使用的
    • wait的工作原理就是wait的時(shí)候,會(huì)進(jìn)入同步塊(synchronized)所對(duì)應(yīng)的條件等待隊(duì)列,在其他地方使用這個(gè)關(guān)鍵字是不可進(jìn)入的
    • 或者說(shuō)wait所對(duì)應(yīng)的管程的入口在synchronied處
  • wait與sleep區(qū)別是什么? @見(jiàn)上面的筆記
最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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