免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
面經(jīng)手冊(cè) · 第16篇《碼農(nóng)會(huì)鎖,ReentrantLock之公平鎖講解和實(shí)現(xiàn)》


作者:小傅哥
博客:https://bugstack.cn

沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!😄

一、前言

Java學(xué)多少才能找到工作?

最近經(jīng)常有小伙伴問(wèn)我,以為我的經(jīng)驗(yàn)來(lái)看,學(xué)多少夠,好像更多的是看你的野心有多大。如果你只是想找個(gè)10k以內(nèi)的二線城市的工作,那還是比較容易的。也不需要學(xué)數(shù)據(jù)結(jié)構(gòu)、也不需要會(huì)算法、也需要懂源碼、更不要有多少項(xiàng)目經(jīng)驗(yàn)。

但反之我遇到一個(gè)國(guó)內(nèi)大學(xué)TOP2畢業(yè)的娃,這貨兼職是Offer收割機(jī):騰訊、阿里、字節(jié)還有國(guó)外新加坡的工作機(jī)會(huì)等等,薪資待遇也是賊高,可能超過(guò)你對(duì)白菜價(jià)的認(rèn)知。上學(xué)無(wú)用、學(xué)習(xí)無(wú)用,純屬扯淡!

你能在這條路上能付出的越多,能努力的越早,收獲就會(huì)越大!

二、面試題

謝飛機(jī),小記,剛?cè)ザ屠萃昴_放松的飛機(jī),因?yàn)槟涂艘m子丟了,罵罵咧咧的赴約面試官。

面試官:咋了,飛機(jī),怎么看上去不高興。

謝飛機(jī):沒(méi)事,沒(méi)事,我心思我學(xué)的 synchronized 呢!

面試官:那正好,飛機(jī)你會(huì)鎖嗎?

謝飛機(jī):啊。。。我沒(méi)去會(huì)所呀!!!你咋知道

面試官:我說(shuō) Java 鎖,你想啥呢!你了解公平鎖嗎,知道怎么實(shí)現(xiàn)的嗎,給我說(shuō)說(shuō)!

謝飛機(jī):公平鎖!?嗯,是不 ReentrantLock 中用到了,我怎么感覺似乎有印象,但是不記得了。

面試官:哎,回家搜搜 CLH 吧!

三、ReentrantLock 和 公平鎖

1. ReentrantLock 介紹

鑒于上一篇小傅哥已經(jīng)基于,HotSpot虛擬機(jī)源碼分析 synchronized 實(shí)現(xiàn)和相應(yīng)核心知識(shí)點(diǎn),本來(lái)想在本章直接介紹下 ReentrantLock。但因?yàn)?ReentrantLock 知識(shí)點(diǎn)較多,因此會(huì)分幾篇分別講解,突出每一篇重點(diǎn),避免豬八戒吞棗。

介紹:ReentrantLock 是一個(gè)可重入且獨(dú)占式鎖,具有與 synchronized 監(jiān)視器(monitor enter、monitor exit)鎖基本相同的行為和語(yǔ)意。但與 synchronized 相比,它更加靈活、強(qiáng)大、增加了輪訓(xùn)、超時(shí)、中斷等高級(jí)功能以及可以創(chuàng)建公平和非公平鎖。

2. ReentrantLock 知識(shí)鏈條

ReentrantLock 是基于 Lock 實(shí)現(xiàn)的可重入鎖,所有的 Lock 都是基于 AQS 實(shí)現(xiàn)的,AQS 和 Condition 各自維護(hù)不同的對(duì)象,在使用 Lock 和 Condition 時(shí),其實(shí)就是兩個(gè)隊(duì)列的互相移動(dòng)。它所提供的共享鎖、互斥鎖都是基于對(duì) state 的操作。而它的可重入是因?yàn)閷?shí)現(xiàn)了同步器 Sync,在 Sync 的兩個(gè)實(shí)現(xiàn)類中,包括了公平鎖和非公平鎖。

這個(gè)公平鎖的具體實(shí)現(xiàn),就是我們本章節(jié)要介紹的重點(diǎn),了解什么是公平鎖、公平鎖的具體實(shí)現(xiàn)。學(xué)習(xí)完基礎(chǔ)的知識(shí)可以更好的理解 ReentrantLock

3. ReentrantLock 公平鎖代碼

3.1 初始化

ReentrantLock lock = new ReentrantLock(true);  // true:公平鎖
lock.lock();
try {
    // todo
} finally {
    lock.unlock();
}
  • 初始化構(gòu)造函數(shù)入?yún)?#xff0c;選擇是否為初始化公平鎖。
  • 其實(shí)一般情況下并不需要公平鎖,除非你的場(chǎng)景中需要保證順序性。
  • 使用 ReentrantLock 切記需要在 finally 中關(guān)閉,lock.unlock()。

3.2 公平鎖、非公平鎖,選擇

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
  • 構(gòu)造函數(shù)中選擇公平鎖(FairSync)、非公平鎖(NonfairSync)。

3.3 hasQueuedPredecessors

static final class FairSync extends Sync {

    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
...
    }
}
  • 公平鎖和非公平鎖,主要是在方法 tryAcquire 中,是否有 !hasQueuedPredecessors() 判斷。

3.4 隊(duì)列首位判斷

public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}
  • 在這個(gè)判斷中主要就是看當(dāng)前線程是不是同步隊(duì)列的首位,是:true、否:false
  • 這部分就涉及到了公平鎖的實(shí)現(xiàn),CLH(Craig,Landin andHagersten)。三個(gè)作者的首字母組合

四、什么是公平鎖

公平鎖就像是馬路邊上的衛(wèi)生間,上廁所需要排隊(duì)。當(dāng)然如果有人不排隊(duì),那么就是非公平鎖了,比如領(lǐng)導(dǎo)要先上。

CLH 是一種基于單向鏈表的高性能、公平的自旋鎖。AQS中的隊(duì)列是CLH變體的虛擬雙向隊(duì)列(FIFO),AQS是通過(guò)將每條請(qǐng)求共享資源的線程封裝成一個(gè)節(jié)點(diǎn)來(lái)實(shí)現(xiàn)鎖的分配。

為了更好的學(xué)習(xí)理解 CLH 的原理,就需要有實(shí)踐的代碼。接下來(lái)一 CLH 為核心分別介紹4種公平鎖的實(shí)現(xiàn),從而掌握最基本的技術(shù)棧知識(shí)。

五、公平鎖實(shí)現(xiàn)

1. CLH

1.1 看圖說(shuō)話

1.2 代碼實(shí)現(xiàn)

public class CLHLock implements Lock {

    private final ThreadLocal<CLHLock.Node> prev;
    private final ThreadLocal<CLHLock.Node> node;
    private final AtomicReference<CLHLock.Node> tail = new AtomicReference<>(new CLHLock.Node());

    private static class Node {
        private volatile boolean locked;
    }

    public CLHLock() {
        this.prev = ThreadLocal.withInitial(() -> null);
        this.node = ThreadLocal.withInitial(CLHLock.Node::new);
    }

    @Override
    public void lock() {
        final Node node = this.node.get();
        node.locked = true;
        Node pred_node = this.tail.getAndSet(node);
        this.prev.set(pred_node);
        // 自旋
        while (pred_node.locked);
    }

    @Override
    public void unlock() {
        final Node node = this.node.get();
        node.locked = false;
        this.node.set(this.prev.get());
    }

}

1.3 代碼講解

CLH(Craig,Landin and Hagersten),是一種基于鏈表的可擴(kuò)展、高性能、公平的自旋鎖。

在這段代碼的實(shí)現(xiàn)過(guò)程中,相當(dāng)于是虛擬出來(lái)一個(gè)鏈表結(jié)構(gòu),由 AtomicReference 的方法 getAndSet 進(jìn)行銜接。getAndSet 獲取當(dāng)前元素,設(shè)置新的元素

lock()

  • 通過(guò) this.node.get() 獲取當(dāng)前節(jié)點(diǎn),并設(shè)置 locked 為 true。
  • 接著調(diào)用 this.tail.getAndSet(node),獲取當(dāng)前尾部節(jié)點(diǎn) pred_node,同時(shí)把新加入的節(jié)點(diǎn)設(shè)置成尾部節(jié)點(diǎn)。
  • 之后就是把 this.prev 設(shè)置為之前的尾部節(jié)點(diǎn),也就相當(dāng)于鏈路的指向。
  • 最后就是自旋 while (pred_node.locked),直至程序釋放。

unlock()

  • 釋放鎖的過(guò)程就是拆鏈,把釋放鎖的節(jié)點(diǎn)設(shè)置為false node.locked = false
  • 之后最重要的是把當(dāng)前節(jié)點(diǎn)設(shè)置為上一個(gè)節(jié)點(diǎn),這樣就相當(dāng)于把自己的節(jié)點(diǎn)拆下來(lái)了,等著垃圾回收。

CLH隊(duì)列鎖的優(yōu)點(diǎn)是空間復(fù)雜度低,在SMP(Symmetric Multi-Processor)對(duì)稱多處理器結(jié)構(gòu)(一臺(tái)計(jì)算機(jī)由多個(gè)CPU組成,并共享內(nèi)存和其他資源,所有的CPU都可以平等地訪問(wèn)內(nèi)存、I/O和外部中斷)效果還是不錯(cuò)的。但在 NUMA(Non-Uniform Memory Access) 下效果就不太好了,這部分知識(shí)可以自行擴(kuò)展。

2. MCSLock

2.1 代碼實(shí)現(xiàn)

public class MCSLock implements Lock {

    private AtomicReference<MCSLock.Node> tail = new AtomicReference<>(null);
    ;
    private ThreadLocal<MCSLock.Node> node;

    private static class Node {
        private volatile boolean locked = false;
        private volatile Node next = null;
    }

    public MCSLock() {
        node = ThreadLocal.withInitial(Node::new);
    }

    @Override
    public void lock() {
        Node node = this.node.get();
        Node preNode = tail.getAndSet(node);
        if (null == preNode) {
            node.locked = true;
            return;
        }
        node.locked = false;
        preNode.next = node;
        while (!node.locked) ;
    }

    @Override
    public void unlock() {
        Node node = this.node.get();
        if (null != node.next) {
            node.next.locked = true;
            node.next = null;
            return;
        }
        if (tail.compareAndSet(node, null)) {
            return;
        }
        while (node.next == null) ;
    }

}

2.1 代碼講解

MCS 來(lái)自于發(fā)明人名字的首字母: John Mellor-Crummey和Michael Scott。

它也是一種基于鏈表的可擴(kuò)展、高性能、公平的自旋鎖,但與 CLH 不同。它是真的有下一個(gè)節(jié)點(diǎn) next,添加這個(gè)真實(shí)節(jié)點(diǎn)后,它就可以只在本地變量上自旋,而 CLH 是前驅(qū)節(jié)點(diǎn)的屬性上自旋。

因?yàn)樽孕?jié)點(diǎn)的不同,導(dǎo)致 CLH 更適合于 SMP 架構(gòu)、MCS 可以適合 NUMA 非一致存儲(chǔ)訪問(wèn)架構(gòu)。你可以想象成 CLH 更需要線程數(shù)據(jù)在同一塊內(nèi)存上效果才更好,MCS 因?yàn)槭窃诒镜曜兞孔赃x,所以無(wú)論數(shù)據(jù)是否分散在不同的CPU模塊都沒(méi)有影響。

代碼實(shí)現(xiàn)上與 CLH 沒(méi)有太多差異,這里就不在敘述了,可以看代碼學(xué)習(xí)。

3. TicketLock

3.1 看圖說(shuō)話

3.2 代碼實(shí)現(xiàn)

public class TicketLock implements Lock {

    private AtomicInteger serviceCount = new AtomicInteger(0);
    private AtomicInteger ticketCount = new AtomicInteger(0);
    private final ThreadLocal<Integer> owner = new ThreadLocal<>();

    @Override
    public void lock() {
        owner.set(ticketCount.getAndIncrement());
        while (serviceCount.get() != owner.get());
    }

    @Override
    public void unlock() {
        serviceCount.compareAndSet(owner.get(), owner.get() + 1);
        owner.remove();
    }
}

3.3 代碼講解

TicketLock 就像你去銀行、呷哺給你的一個(gè)排號(hào)卡一樣,叫到你號(hào)你才能進(jìn)去。屬于嚴(yán)格的公平性實(shí)現(xiàn),但是多處理器系統(tǒng)上,每個(gè)進(jìn)程/線程占用的處理器都在讀寫同一個(gè)變量,每次讀寫操作都需要進(jìn)行多處理間的緩存同步,非常消耗系統(tǒng)性能。

代碼實(shí)現(xiàn)上也比較簡(jiǎn)單,lock() 中設(shè)置擁有者的號(hào)牌,并進(jìn)入自旋比對(duì)。unlock() 中使用 CAS 進(jìn)行解鎖操作,并處理移除。

六、總結(jié)

  • ReentrantLock 是基于 Lock 實(shí)現(xiàn)的可重入鎖,對(duì)于公平鎖 CLH 的實(shí)現(xiàn),只是這部分知識(shí)的冰山一角,但有這一,就可以很好熱身便于后續(xù)的學(xué)習(xí)。
  • ReentrantLock 使用起來(lái)更加靈活,可操作性也更大,但一定要在 finally 中釋放鎖,目的是保證在獲取鎖之后,最終能夠被釋放。同時(shí)不要將獲取鎖的過(guò)程寫在 try 里面。
  • 公平鎖的實(shí)現(xiàn)依據(jù)不同場(chǎng)景和SMP、NUMA的使用,會(huì)有不同的優(yōu)劣效果。在實(shí)際的使用中一般默認(rèn)會(huì)選擇非公平鎖,即使是自旋也是耗費(fèi)性能的,一般會(huì)用在較少等待的線程中,避免自旋時(shí)過(guò)長(zhǎng)。

七、系列推薦

  • synchronized 解毒,剖析源碼深度分析!
  • 面試官,ThreadLocal 你要這么問(wèn),我就掛了!
  • 掃盲java.util.Collections工具包,學(xué)習(xí)排序、二分、洗牌、旋轉(zhuǎn)算法
  • HashMap核心知識(shí),擾動(dòng)函數(shù)、負(fù)載因子、擴(kuò)容鏈表拆分
  • Netty+JavaFx實(shí)戰(zhàn):仿桌面版微信聊天!
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
圖解AQS源碼分析
轉(zhuǎn)載:從synchronized 到CAS 和 AQS徹底弄懂Java各種并發(fā)鎖
一篇干貨技術(shù)文,AQS介紹與源碼剖析
我畫了35張圖就是為了讓你深入 AQS
萬(wàn)字超強(qiáng)圖文講解 AQS 以及 ReentrantLock 應(yīng)用
快速掌握并發(fā)編程---細(xì)說(shuō)ReentrantLock和AQS
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服