国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

【Java并發(fā)編程實(shí)戰(zhàn)】-----“J.U.C”:ReentrantLock之二lock方法分析

2019-11-15 01:11:54
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
java并發(fā)編程實(shí)戰(zhàn)】-----“J.U.C”:ReentrantLock之二lock方法分析

前一篇博客簡(jiǎn)單介紹了ReentrantLock的定義和與synchronized的區(qū)別,下面跟隨LZ的筆記來(lái)扒扒ReentrantLock的lock方法。我們知道ReentrantLock有公平鎖、非公平鎖之分,所以lock()我也已公平鎖、非公平鎖來(lái)進(jìn)行闡述。首先我們來(lái)看ReentrantLock的結(jié)構(gòu)【圖來(lái)自Java多線(xiàn)程系列--“JUC鎖”03之 公平鎖(一)】:

2015073100001

從上圖我們可以看到,ReentrantLock實(shí)現(xiàn)Lock接口,Sync與ReentrantLock是組合關(guān)系,且FairSync(公平鎖)、NonfairySync(非公平鎖)是Sync的子類(lèi)。Sync繼承AQS(AbstractQueuedSynchronizer)。在具體分析lock時(shí),我們需要了解幾個(gè)概念:

AQS(AbstractQueuedSynchronizer):為java中管理鎖的抽象類(lèi)。該類(lèi)為實(shí)現(xiàn)依賴(lài)于先進(jìn)先出 (FIFO) 等待隊(duì)列的阻塞鎖和相關(guān)同步器(信號(hào)量、事件,等等)提供一個(gè)框架。該類(lèi)提供了一個(gè)非常重要的機(jī)制,在JDK API中是這樣描述的:為實(shí)現(xiàn)依賴(lài)于先進(jìn)先出 (FIFO) 等待隊(duì)列的阻塞鎖和相關(guān)同步器(信號(hào)量、事件,等等)提供一個(gè)框架。此類(lèi)的設(shè)計(jì)目標(biāo)是成為依靠單個(gè)原子 int 值來(lái)表示狀態(tài)的大多數(shù)同步器的一個(gè)有用基礎(chǔ)。子類(lèi)必須定義更改此狀態(tài)的受保護(hù)方法,并定義哪種狀態(tài)對(duì)于此對(duì)象意味著被獲取或被釋放。假定這些條件之后,此類(lèi)中的其他方法就可以實(shí)現(xiàn)所有排隊(duì)和阻塞機(jī)制。子類(lèi)可以維護(hù)其他狀態(tài)字段,但只是為了獲得同步而只追蹤使用 getState()、setState(int) 和 compareAndSetState(int, int) 方法來(lái)操作以原子方式更新的 int 值。 這么長(zhǎng)的話(huà)用一句話(huà)概括就是:維護(hù)鎖的當(dāng)前狀態(tài)和線(xiàn)程等待列表。

CLH:AQS中“等待鎖”的線(xiàn)程隊(duì)列。我們知道在多線(xiàn)程環(huán)境中我們?yōu)榱吮Wo(hù)資源的安全性常使用鎖將其保護(hù)起來(lái),同一時(shí)刻只能有一個(gè)線(xiàn)程能夠訪(fǎng)問(wèn),其余線(xiàn)程則需要等待,CLH就是管理這些等待鎖的隊(duì)列。

CAS(compare and swap):比較并交換函數(shù),它是原子操作函數(shù),也就是說(shuō)所有通過(guò)CAS操作的數(shù)據(jù)都是以原子方式進(jìn)行的。

公平鎖(FairSync):lock

lock()定義如下:

final void lock() {            acquire(1);        }

lock()內(nèi)部調(diào)用acquire(1),為何是”1”呢?首先我們知道ReentrantLock是獨(dú)占鎖,1表示的是鎖的狀態(tài)state。對(duì)于獨(dú)占鎖而言,如果所處于可獲取狀態(tài),其狀態(tài)為0,當(dāng)鎖初次被線(xiàn)程獲取時(shí)狀態(tài)變成1。

acquire()是AbstractQueuedSynchronizer中的方法,其源碼如下:

public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }

從該方法的實(shí)現(xiàn)中我們可以看出,它做了非常多的工作,具體工作我們先晾著,先看這些方法的實(shí)現(xiàn):

tryAcquire

tryAcquire方法是在FairySync中實(shí)現(xiàn)的,其源代碼如下:

PRotected final boolean tryAcquire(int acquires) {        //當(dāng)前線(xiàn)程        final Thread current = Thread.currentThread();        //獲取鎖狀態(tài)state        int c = getState();        /*         * 當(dāng)c==0表示鎖沒(méi)有被任何線(xiàn)程占用,在該代碼塊中主要做如下幾個(gè)動(dòng)作:         * 則判斷“當(dāng)前線(xiàn)程”是不是CLH隊(duì)列中的第一個(gè)線(xiàn)程線(xiàn)程(hasQueuedPredecessors),         * 若是的話(huà),則獲取該鎖,設(shè)置鎖的狀態(tài)(compareAndSetState),         * 并切設(shè)置鎖的擁有者為“當(dāng)前線(xiàn)程”(setExclusiveOwnerThread)。         */        if (c == 0) {            if (!hasQueuedPredecessors() &&                compareAndSetState(0, acquires)) {                setExclusiveOwnerThread(current);                return true;            }        }        /*         * 如果c != 0,表示該鎖已經(jīng)被線(xiàn)程占有,則判斷該鎖是否是當(dāng)前線(xiàn)程占有,若是設(shè)置state,否則直接返回false         */        else if (current == getExclusiveOwnerThread()) {            int nextc = c + acquires;            if (nextc < 0)                throw new Error("Maximum lock count exceeded");            setState(nextc);            return true;        }        return false;    }

在這里我們可以肯定tryAcquire主要是去嘗試獲取鎖,獲取成功則設(shè)置鎖狀態(tài)并返回true,否則返回false。

hasQueuedPredecessors:"當(dāng)前線(xiàn)程"是不是在CLH隊(duì)列的隊(duì)首,來(lái)返回AQS中是不是有比“當(dāng)前線(xiàn)程”等待更久的線(xiàn)程(公平鎖)。

public final boolean hasQueuedPredecessors() {        Node t = tail;         Node h = head;            Node s;        return h != t &&            ((s = h.next) == null || s.thread != Thread.currentThread());    }

Node是AbstractQueuedSynchronizer的內(nèi)部類(lèi),它代表著CLH列表的一個(gè)線(xiàn)程節(jié)點(diǎn)。對(duì)于Node以后LZ會(huì)詳細(xì)闡述的。

compareAndSetState:設(shè)置鎖狀態(tài)

protected final boolean compareAndSetState(int expect, int update) {            return unsafe.compareAndSwapInt(this, stateOffset, expect, update);        }

compareAndSwapInt() 是sun.misc.Unsafe類(lèi)中的一個(gè)本地方法。對(duì)此,我們需要了解的是 compareAndSetState(expect, update) 是以原子的方式操作當(dāng)前線(xiàn)程;若當(dāng)前線(xiàn)程的狀態(tài)為expect,則設(shè)置它的狀態(tài)為update。

setExclusiveOwnerThread:設(shè)置當(dāng)前線(xiàn)程為鎖的擁有者

protected final void setExclusiveOwnerThread(Thread t) {        exclusiveOwnerThread = t;    }
addWaiter(Node.EXCLUSIVE)
private Node addWaiter(Node mode) {        //new 一個(gè)Node節(jié)點(diǎn)        Node node = new Node(Thread.currentThread(), mode);                //CLH隊(duì)列尾節(jié)點(diǎn)        Node pred = tail;                //CLH尾節(jié)點(diǎn)!= null,表示CLH隊(duì)列 != null,則將線(xiàn)程加入到CLH隊(duì)列隊(duì)尾        if (pred != null) {            node.prev = pred;            if (compareAndSetTail(pred, node)) {                pred.next = node;                return node;            }        }        //若CLH隊(duì)列為空,則調(diào)用enq()新建CLH隊(duì)列,然后再將“當(dāng)前線(xiàn)程”添加到CLH隊(duì)列中。        enq(node);        return node;    }

addWaiter()主要是將當(dāng)前線(xiàn)程加入到CLH隊(duì)列隊(duì)尾。其中compareAndSetTail和enq的源代碼如下:

/**     * 判斷CLH隊(duì)列的隊(duì)尾是不是為expect,是的話(huà),就將隊(duì)尾設(shè)為update     * @param expect     * @param update     * @return     */    private final boolean compareAndSetTail(Node expect, Node update) {        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);    }        /**     * 如果CLH隊(duì)列為空,則新建一個(gè)CLH表頭;然后將node添加到CLH末尾。否則,直接將node添加到CLH末尾     * @param node     * @return     */    private Node enq(final Node node) {        for (;;) {            Node t = tail;            if (t == null) {                     if (compareAndSetHead(new Node()))                    tail = head;            } else {                node.prev = t;                if (compareAndSetTail(t, node)) {                    t.next = node;                    return t;                }            }        }    }

addWaiter的實(shí)現(xiàn)比較簡(jiǎn)單且實(shí)現(xiàn)功能明了:當(dāng)前線(xiàn)程加入到CLH隊(duì)列隊(duì)尾。

acquireQueued
final boolean acquireQueued(final Node node, int arg) {        boolean failed = true;        try {            //線(xiàn)程中斷標(biāo)志位            boolean interrupted = false;            for (;;) {                //上一個(gè)節(jié)點(diǎn),因?yàn)閚ode相當(dāng)于當(dāng)前線(xiàn)程,所以上一個(gè)節(jié)點(diǎn)表示“上一個(gè)等待鎖的線(xiàn)程”                final Node p = node.predecessor();                /*                 * 如果當(dāng)前線(xiàn)程是head的直接后繼則嘗試獲取鎖                 * 這里不會(huì)和等待隊(duì)列中其它線(xiàn)程發(fā)生競(jìng)爭(zhēng),但會(huì)和嘗試獲取鎖且尚未進(jìn)入等待隊(duì)列的線(xiàn)程發(fā)生競(jìng)爭(zhēng)。這是非公平鎖和公平鎖的一個(gè)重要區(qū)別。                 */                if (p == head && tryAcquire(arg)) {                    setHead(node);     //將當(dāng)前節(jié)點(diǎn)設(shè)置設(shè)置為頭結(jié)點(diǎn)                    p.next = null;                     failed = false;                    return interrupted;                }                /* 如果不是head直接后繼或獲取鎖失敗,則檢查是否要阻塞當(dāng)前線(xiàn)程,是則阻塞當(dāng)前線(xiàn)程                 * shouldParkAfterFailedAcquire:判斷“當(dāng)前線(xiàn)程”是否需要阻塞                 * parkAndCheckInterrupt:阻塞當(dāng)前線(xiàn)程                 */                if (shouldParkAfterFailedAcquire(p, node) &&                    parkAndCheckInterrupt())                    interrupted = true;            }        } finally {            if (failed)                cancelAcquire(node);             }    }

在這個(gè)for循環(huán)中,LZ不是很明白為什么要加p==head,Java多線(xiàn)程系列--“JUC鎖”03之 公平鎖(一)這篇博客有一個(gè)較好的解釋如下:

p == head && tryAcquire(arg) 首先,判斷“前繼節(jié)點(diǎn)”是不是CHL表頭。如果是的話(huà),則通過(guò)tryAcquire()嘗試獲取鎖。 其實(shí),這樣做的目的是為了“讓當(dāng)前線(xiàn)程獲取鎖”,但是為什么需要先判斷p==head呢?理解這個(gè)對(duì)理解“公平鎖”的機(jī)制很重要,因?yàn)檫@么做的原因就是為了保證公平性!       (a) 前面,我們?cè)趕houldParkAfterFailedAcquire()我們判斷“當(dāng)前線(xiàn)程”是否需要阻塞;       (b) 接著,“當(dāng)前線(xiàn)程”阻塞的話(huà),會(huì)調(diào)用parkAndCheckInterrupt()來(lái)阻塞線(xiàn)程。當(dāng)線(xiàn)程被解除阻塞的時(shí)候,我們會(huì)返回線(xiàn)程的中斷狀態(tài)。而線(xiàn)程被解決阻塞,可能是由于“線(xiàn)程被中斷”,也可能是由于“其它線(xiàn)程調(diào)用了該線(xiàn)程的unpark()函數(shù)”。       (c) 再回到p==head這里。如果當(dāng)前線(xiàn)程是因?yàn)槠渌€(xiàn)程調(diào)用了unpark()函數(shù)而被喚醒,那么喚醒它的線(xiàn)程,應(yīng)該是它的前繼節(jié)點(diǎn)所對(duì)應(yīng)的線(xiàn)程(關(guān)于這一點(diǎn),后面在“釋放鎖”的過(guò)程中會(huì)看到)。 OK,是前繼節(jié)點(diǎn)調(diào)用unpark()喚醒了當(dāng)前線(xiàn)程! 此時(shí),再來(lái)理解p==head就很簡(jiǎn)單了:當(dāng)前繼節(jié)點(diǎn)是CLH隊(duì)列的頭節(jié)點(diǎn),并且它釋放鎖之后;就輪到當(dāng)前節(jié)點(diǎn)獲取鎖了。然后,當(dāng)前節(jié)點(diǎn)通過(guò)tryAcquire()獲取鎖;獲取成功的話(huà),通過(guò)setHead(node)設(shè)置當(dāng)前節(jié)點(diǎn)為頭節(jié)點(diǎn),并返回。        總之,如果“前繼節(jié)點(diǎn)調(diào)用unpark()喚醒了當(dāng)前線(xiàn)程”并且“前繼節(jié)點(diǎn)是CLH表頭”,此時(shí)就是滿(mǎn)足p==head,也就是符合公平性原則的。否則,如果當(dāng)前線(xiàn)程是因?yàn)椤熬€(xiàn)程被中斷”而喚醒,那么顯然就不是公平了。這就是為什么說(shuō)p==head就是保證公平性!

在該方法中有兩個(gè)方法比較重要,shouldParkAfterFailedAcquire和parkAndCheckInterrupt,其中

shouldParkAfterFailedAcquire:判斷“當(dāng)前線(xiàn)程”是否需要阻塞,源碼如下:

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {           //當(dāng)前節(jié)點(diǎn)的狀態(tài)            int ws = pred.waitStatus;            if (ws == Node.SIGNAL)                return true;            if (ws > 0) {                do {                    node.prev = pred = pred.prev;                } while (pred.waitStatus > 0);                pred.next = node;            } else {                compareAndSetWaitStatus(pred, ws, Node.SIGNAL);            }            return false;        }

waitStatus是節(jié)點(diǎn)Node定義的,她是標(biāo)識(shí)線(xiàn)程的等待狀態(tài),他主要有如下四個(gè)值:

CANCELLED = 1:線(xiàn)程已被取消;

SIGNAL = -1:當(dāng)前線(xiàn)程的后繼線(xiàn)程需要被unpark(喚醒);

CONDITION = -2 :線(xiàn)程(處在Condition休眠狀態(tài))在等待Condition喚醒;

PROPAGATE = –3:(共享鎖)其它線(xiàn)程獲取到“共享鎖”.

有了這四個(gè)狀態(tài),我們?cè)賮?lái)分析上面代碼,當(dāng)ws == SIGNAL時(shí)表明當(dāng)前節(jié)點(diǎn)需要unpark(喚醒),直接返回true,當(dāng)ws > 0 (CANCELLED),表明當(dāng)前節(jié)點(diǎn)已經(jīng)被取消了,則通過(guò)回溯的方法(do{}while())向前找到一個(gè)非CANCELLED的節(jié)點(diǎn)并返回false。其他情況則設(shè)置該節(jié)點(diǎn)為SIGNAL狀態(tài)。我們?cè)倩氐絠f (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()),p是當(dāng)前節(jié)點(diǎn)的前繼節(jié)點(diǎn),當(dāng)該前繼節(jié)點(diǎn)狀態(tài)為SIGNAL時(shí)返回true,表示當(dāng)前線(xiàn)程需要阻塞,則調(diào)用parkAndCheckInterrupt()阻塞當(dāng)前線(xiàn)程。

parkAndCheckInterrupt:阻塞當(dāng)前線(xiàn)程,并且返回“線(xiàn)程被喚醒之后”的中斷狀態(tài),源碼如下:

private final boolean parkAndCheckInterrupt() {    //通過(guò)LockSupport的park()阻塞“當(dāng)前線(xiàn)程”。        LockSupport.park(this);        return Thread.interrupted();    }

從上面我們可以總結(jié),acquireQueued()是當(dāng)前線(xiàn)程會(huì)根據(jù)公平性原則來(lái)進(jìn)行阻塞等待,直到獲取鎖為止;并且返回當(dāng)前線(xiàn)程在等待過(guò)程中有沒(méi)有并中斷過(guò)。

selfInterrupt
private static void selfInterrupt() {        Thread.currentThread().interrupt();    }

selfInterrupt()產(chǎn)生一個(gè)中斷。如果在acquireQueued()中當(dāng)前線(xiàn)程被中斷過(guò),則需要產(chǎn)生一個(gè)中斷。

Fairy lock()總結(jié)

我們?cè)倏碼cquire()源碼:

public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }

首先通過(guò)tryAcquire方法嘗試獲取鎖,如果成功直接返回,否則通過(guò)acquireQueued()再次獲取。在acquireQueued()中會(huì)先通過(guò)addWaiter將當(dāng)前線(xiàn)程加入到CLH隊(duì)列的隊(duì)尾,在CLH隊(duì)列中等待。在等待過(guò)程中線(xiàn)程處于休眠狀態(tài),直到成功獲取鎖才會(huì)返回。如下:

2015081100001

非公平鎖(NonfairSync):lock

非公平鎖NonfairSync的lock()與公平鎖的lock()在獲取鎖的流程上是一直的,但是由于它是非公平的,所以獲取鎖機(jī)制還是有點(diǎn)不同。通過(guò)前面我們了解到公平鎖在獲取鎖時(shí)采用的是公平策略(CLH隊(duì)列),而非公平鎖則采用非公平策略它無(wú)視等待隊(duì)列,直接嘗試獲取。如下:

final void lock() {            if (compareAndSetState(0, 1))                setExclusiveOwnerThread(Thread.currentThread());            else                acquire(1);        }

lock()通過(guò)compareAndSetState嘗試設(shè)置所狀態(tài),若成功直接將鎖的擁有者設(shè)置為當(dāng)前線(xiàn)程(簡(jiǎn)單粗暴),否則調(diào)用acquire()嘗試獲取鎖;

acquire
public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }

在非公平鎖中acquire()的實(shí)現(xiàn)和公平鎖一模一樣,但是他們嘗試獲取鎖的機(jī)制不同(也就是tryAcquire()的實(shí)現(xiàn)不同)。

protected final boolean tryAcquire(int acquires) {            return nonfairTryAcquire(acquires);        }

tryAcquire內(nèi)部調(diào)用nonfairyTryAcquire:

final boolean nonfairTryAcquire(int acquires) {            final Thread current = Thread.currentThread();            int c = getState();            if (c == 0) {                if (compareAndSetState(0, acquires)) {                    setExclusiveOwnerThread(current);                    return true;                }            }            else if (current == getExclusiveOwnerThread()) {                int nextc = c + acquires;                if (nextc < 0) // overflow                    throw new Error("Maximum lock count exceeded");                setState(nextc);                return true;            }            return false;        }

與公平鎖相比,非公平鎖的不同之處就體現(xiàn)在if(c==0)的條件代碼塊中:

//----------------非公平鎖-----    if (c == 0) {                if (compareAndSetState(0, acquires)) {                    setExclusiveOwnerThread(current);                    return true;                }            }  //----------------公平鎖-----   if (c == 0) {                if (!hasQueuedPredecessors() &&                    compareAndSetState(0, acquires)) {                    setExclusiveOwnerThread(current);                    return true;                }            }

是否已經(jīng)發(fā)現(xiàn)了不同之處。公平鎖中要通過(guò)hasQueuedPredecessors()來(lái)判斷該線(xiàn)程是否位于CLH隊(duì)列中頭部,是則獲取鎖;而非公平鎖則不管你在哪個(gè)位置都直接獲取鎖。

參考文獻(xiàn):

1、Java多線(xiàn)程系列--“JUC鎖”03之 公平鎖(一)

2、ReentrantLock源碼之一lock方法解析(鎖的獲取)


發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 惠来县| 宁武县| 新建县| 清流县| 鄄城县| 原阳县| 松阳县| 大城县| 仲巴县| 辽源市| 柞水县| 平和县| 固镇县| 繁昌县| 衡东县| 波密县| 西青区| 临汾市| 昌吉市| 江华| 额济纳旗| 常山县| 广汉市| 建水县| 内乡县| 永清县| 绿春县| 彰武县| 汉中市| 玉林市| 永济市| 佛冈县| 固安县| 江源县| 砀山县| 廉江市| 宁远县| 方城县| 盐城市| 元江| 密云县|