欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java開發(fā)非公平鎖不可打斷源碼示例解析

 更新時間:2023年02月22日 16:03:38   作者:Emanon  
這篇文章主要為大家介紹了java開發(fā)非公平鎖不可打斷源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

非公平鎖不可打斷調(diào)試代碼

    package test;
    import java.util.concurrent.locks.ReentrantLock;
    public class TestReenTrantLock {
        public static void main(String[] args) throws InterruptedException {
            ReentrantLock lock = new ReentrantLock();
            new Thread(() -> {
                System.out.println("1 start");
                lock.lock();
                System.out.println("1 entry");
                try {
                    Thread.sleep(1000 * 60 * 10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            },"t1").start();
            Thread.sleep(2000 );
            new Thread(() -> {
                System.out.println("2 start");
                lock.lock();
                System.out.println("2 entry");
                try {
                } finally {
                    lock.unlock();
                }
            },"t2").start();
        }
    }

保證線程1先獲取到鎖,睡眠10分鐘,因為需要打斷點(diǎn),線程2再去獲取鎖。

非公平鎖不可打斷加鎖源碼

lock

public void lock() {
    sync.lock();
}
final void lock() {
    //首先用線程1使用 cas 嘗試將 state 從 0 改為 1,如果成功表示獲得了鎖
    //因為線程1獲取到了鎖state現(xiàn)在等于1,所以此時線程2獲取鎖失敗。 
    //線程2執(zhí)行acquire(1);
    //非公平的體現(xiàn):上來就加鎖
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

acquire

//arg = 1
public final void acquire(int arg) {
    //線程2執(zhí)行tryAcquire(arg),返回false代表鎖獲取失敗,!tryAcquire(arg) ==true
    //由于是&&判斷
    //所以線程2調(diào)用addWaiter做尾部入隊操作
    //線程2接著調(diào)用acquireQueued進(jìn)入park阻塞
    if (!tryAcquire(arg) &&
        //addWaiter(Node.EXCLUSIVE) 返回的是 線程2的所在的Node節(jié)點(diǎn)
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){
        //acquireQueued方法返回的是打斷標(biāo)志 如果阻塞狀態(tài)或者運(yùn)行狀態(tài)被打斷
        //返回true 那么會執(zhí)行selfInterrupt自我打斷 
        //selfInterrupt方法只有1句代碼:Thread.currentThread().interrupt();
        selfInterrupt();
    }
}

tryAcquire:嘗試加鎖&判斷鎖重入

//acquires=1
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
//此時 線程2累計嘗試2次加鎖
final boolean nonfairTryAcquire(int acquires) {
    //acquires=1
    final Thread current = Thread.currentThread();
    int c = getState();
    //如果線程1已經(jīng)釋放鎖 此時c==0滿足 會再次使用cas嘗試加鎖
    //這里線程1仍然持有鎖 條件不滿足
    if (c == 0) {
        // 嘗試用 cas 獲得, 這里體現(xiàn)了非公平性: 不去檢查 AQS 隊列
        // 非公平鎖可以提高并發(fā)度,但是會導(dǎo)致饑餓,可以使用超時時間解決饑餓
        // 線程切換的開銷,其實就是非公平鎖效率高于公平鎖的原因
        // 因為非公平鎖減少了線程掛起的幾率,后來的線程有一定幾率節(jié)省被掛起的開銷
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            //代表加鎖成功
            return true;
        }
    }
    // 判斷是否鎖重入
    else if (current == getExclusiveOwnerThread()) {
        //使用原來的state + acquires,這里acquires = 1
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    //返回false代表線程2獲取鎖失敗
    return false;
}

acquireQueued:加入同步隊列

addWaiter方法的第一個參數(shù)是mode,Node.EXCLUSIVE是個null。

static final Node EXCLUSIVE = null;

acquireQueued方法的第一個參數(shù)是node,其實就是線程2所在的Node節(jié)點(diǎn)。第二個參數(shù)是1:代表了本次state加鎖成功累加的數(shù)量。

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

由于acquireQueued方法的參數(shù)是addWaiter方法的返回值,因此先看addWaiter方法

//node是Node.EXCLUSIVE 默認(rèn)是null
//enq方法創(chuàng)建的隊列如下:頭結(jié)點(diǎn)->尾結(jié)點(diǎn)(線程2所在節(jié)點(diǎn))
//后續(xù)的節(jié)點(diǎn)都在addWaiter方法中入隊,不再進(jìn)入enq:頭結(jié)點(diǎn)->尾結(jié)點(diǎn)(線程2所在節(jié)點(diǎn))->尾結(jié)點(diǎn)(線程3所在節(jié)點(diǎn))
private Node addWaiter(Node mode) {
    //static final Node EXCLUSIVE = null;
    //node為持有當(dāng)前線程的node
    //mode為null 可以看到賦值給了 nextWaiter
    //也就是線程2所在節(jié)點(diǎn)的next指針指向了null
    //注意:nextWaiter是等待隊列中的指針
    /***
        Node(Thread thread, Node mode) {      
            this.nextWaiter = mode;
            this.thread = thread;
        }
        ***/
    Node node = new Node(Thread.currentThread(), mode);
    //獲取同步隊列的尾部節(jié)點(diǎn)
    Node pred = tail;
    //此時同步隊列的tail是null,因為到目前為止并沒有執(zhí)行過enq方法
    //如果tail不為null:使用cas嘗試將Node對象插入隊列尾部,作為新的尾結(jié)點(diǎn)
    if (pred != null) {
        //將當(dāng)前node節(jié)點(diǎn)的前一個節(jié)點(diǎn)指向原tail節(jié)點(diǎn) 
        node.prev = pred;
        //將當(dāng)前node節(jié)點(diǎn)作為新的尾節(jié)點(diǎn)
        if (compareAndSetTail(pred, node)) {
            //原來的尾節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)的下一個節(jié)點(diǎn)
            pred.next = node;
            return node;
        }
    }
    //因為tail節(jié)點(diǎn)是null 嘗試將Node加入隊列
    enq(node);
    //返回線程2節(jié)點(diǎn)
    return node;
}
//下面解釋中的當(dāng)前節(jié)點(diǎn)指的是Thread-2所在的節(jié)點(diǎn)
//enq相當(dāng)于是初始化頭尾結(jié)點(diǎn)和第一個入隊的節(jié)點(diǎn)
//只有第1個入隊的節(jié)點(diǎn)才會進(jìn)入該方法
//后續(xù)的線程都會直接執(zhí)行enq(node)之前的代碼加入尾節(jié)點(diǎn)
//enq方法構(gòu)造了1個雙向隊列:頭結(jié)點(diǎn)->尾結(jié)點(diǎn)(線程2所在節(jié)點(diǎn))
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        //第一次進(jìn)入循環(huán) tail是尾節(jié)點(diǎn)為null
        if (t == null) { 
            //第一次進(jìn)入循環(huán):設(shè)置頭結(jié)點(diǎn)為哨兵節(jié)點(diǎn)也叫啞結(jié)點(diǎn):因為沒有對應(yīng)的線程與之關(guān)聯(lián)
            // head節(jié)點(diǎn)的屬性:thread=null
            if (compareAndSetHead(new Node()))
                //第一次進(jìn)入循環(huán):將頭結(jié)點(diǎn)賦值給尾節(jié)點(diǎn) 此時頭和尾是同一個節(jié)點(diǎn) 這點(diǎn)很重要
                tail = head;
        } else {
            //第二次進(jìn)入循環(huán):此處的t就是head,將當(dāng)前節(jié)點(diǎn)的前置指針指向頭節(jié)點(diǎn)
            node.prev = t;
            //第二次進(jìn)入循環(huán):使用cas將尾節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)
            //第二次進(jìn)入循環(huán):此時頭結(jié)點(diǎn)是哨兵節(jié)點(diǎn)(啞結(jié)點(diǎn)),尾節(jié)點(diǎn)即Thread-2所在的線程的節(jié)點(diǎn)
            if (compareAndSetTail(t, node)) {
                //第二次進(jìn)入循環(huán):將head.next指向當(dāng)前節(jié)點(diǎn)那么這個鏈表是雙向鏈表
                t.next = node;
                //循環(huán)結(jié)束
                return t;
            }
        }
    }
}
//node是 線程2的節(jié)點(diǎn)
//arg = 1
//node.predecessor():獲取當(dāng)前節(jié)點(diǎn)的上一個節(jié)點(diǎn) 
//node.predecessor()和node.prev不同的是:
//node.prev如果是null不會拋出異常
//node.predecessor()中如果 node.prev是 null 會拋出異常
//acquireQueued方法返回的是打斷狀態(tài)
final boolean acquireQueued(final Node node, int arg) {
    //node即Thread-2所在的線程的節(jié)點(diǎn)
    boolean failed = true;
    try {
        boolean interrupted = false;
        //死循環(huán)開始
        for (;;) {
            //p是Thread-2所在的線程的節(jié)點(diǎn)的前置節(jié)點(diǎn)即頭結(jié)點(diǎn)
            final Node p = node.predecessor();
            //p == head 即Thread-2所在的線程的節(jié)點(diǎn)的前置節(jié)點(diǎn)是頭結(jié)點(diǎn)
            //tryAcquire(arg) 使用cas再次嘗試獲取鎖 獲取鎖失敗 代碼不進(jìn)入if向下執(zhí)行
            //此時累計嘗試3次
            if (p == head &amp;&amp; tryAcquire(arg)) {
                //如果獲取鎖成功將當(dāng)前節(jié)點(diǎn)設(shè)置為頭結(jié)點(diǎn)并將當(dāng)前節(jié)點(diǎn)的thread屬性和prev屬性設(shè)置為null 
                //也就是當(dāng)前節(jié)點(diǎn)的prev和原來的頭節(jié)點(diǎn)斷開
                //因為當(dāng)前節(jié)點(diǎn)獲取鎖成功,意味著線程1已經(jīng)釋放鎖,此時需要和代表線程1的原來的頭結(jié)點(diǎn)斷開。
                setHead(node);
                //將原來的頭節(jié)點(diǎn)斷開和當(dāng)前節(jié)點(diǎn)的連接 相當(dāng)于原來的節(jié)點(diǎn)出隊
                p.next = null; // help GC
                failed = false;
                //注意這是在死循環(huán)里
                //如果interrupted返回的是true 將會執(zhí)行 selfInterrupt(); 自我中斷
                // if (!tryAcquire(arg) &amp;&amp;acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){selfInterrupt();}
                //即:獲取鎖阻塞的過程中被打斷,也要重新進(jìn)入死循環(huán)一直等到獲取鎖才能執(zhí)行打斷,這就是不可打斷。
                //可打斷是指在等待鎖的過程中,其它線程可以用interrupt方法終止等待,synchronized鎖是不可打斷的。
                //我們要想在等鎖的過程中被打斷,就要使用lockInterruptibly()方法對lock對象加鎖,而不是lock()方法。
                return interrupted;
            }
            //第一次進(jìn)入shouldParkAfterFailedAcquire 
            //將當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)即頭結(jié)點(diǎn)改為-1 返回false (累計嘗試4次)
            //如果當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)以及更前面的節(jié)點(diǎn)有取消的節(jié)點(diǎn) 
            //要斷開這些節(jié)點(diǎn) 包括當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)
            //第二次進(jìn)入shouldParkAfterFailedAcquire 
            //如果當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)是-1 返回true
            //shouldParkAfterFailedAcquire 返回true時
            //會進(jìn)入parkAndCheckInterrupt()方法中,然后會park當(dāng)前線程
            //Thread-2所在node被阻塞,然后等待喚醒,此時node的waitStatus=0
            if (shouldParkAfterFailedAcquire(p, node) &amp;&amp;
                parkAndCheckInterrupt()){
                //在不可打斷模式中
                //線程park在parkAndCheckInterrupt方法里
                //如果線程被打斷,parkAndCheckInterrupt方法返回true
                //執(zhí)行以下代碼
                //Interrupted = true 
                //Interrupted = true 代表被阻塞期間打斷過
                //然后繼續(xù)進(jìn)入死循環(huán)直到獲取鎖 
                //獲取鎖后返回Interrupted = true 
                //最后返回到acquire方法
                //進(jìn)入selfInterrupt();執(zhí)行Thread.currentThread().interrupt();
                //在可打斷模式中
                //線程park在parkAndCheckInterrupt方法里
                //如果線程被打斷,parkAndCheckInterrupt方法返回true
                //執(zhí)行以下代碼
                //throw new InterruptedException();
                interrupted = true;
            }
        }//死循環(huán)結(jié)束
    } finally {
       /**
        這里的failed 什么時候變成true的?
        默認(rèn)的failed=true
        在死循環(huán)一直都是true?。。∫驗橐恢睕]有獲取鎖成功??!
        除非是獲取到了鎖才被賦值為false
         1.try代碼塊拋出異常
        ***/
        if (failed)
            cancelAcquire(node);
    }
}
//node是當(dāng)前節(jié)點(diǎn)
private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

shouldParkAfterFailedAcquire:判斷是否需要park

//p是Thread-2所在節(jié)點(diǎn)的前置節(jié)點(diǎn)即頭結(jié)點(diǎn) 
//node是 Thread-2所在節(jié)點(diǎn)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //p是Thread-2所在節(jié)點(diǎn)的前置節(jié)點(diǎn)即頭結(jié)點(diǎn) 
    //頭結(jié)點(diǎn)的waitStatus=0 
    int ws = pred.waitStatus;
    //第一次進(jìn)入 ws=0 修改waitStatus為-1
    //第二次進(jìn)入ws=-1 Node.SIGNAL=-1 代表等待喚醒  返回true
    if (ws == Node.SIGNAL){
        // 上一個節(jié)點(diǎn)=-1 都在阻塞, 那么自己也阻塞好了
        //返回true代表要park
        return true;
    }
    //如果當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)的waitStatus&gt;0 
    //說明當(dāng)前節(jié)點(diǎn)D的前置節(jié)點(diǎn)C被取消,那么要把當(dāng)前節(jié)點(diǎn)D的前置節(jié)點(diǎn)重新設(shè)置為[當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)C的前置節(jié)點(diǎn)B]
    //B&lt;---C&lt;---D
    //假如B節(jié)點(diǎn)被取消,此時需要斷開C那么直接將D指向B即可
    //A&lt;---B&lt;---C&lt;---D
    //假如BC節(jié)點(diǎn)被取消,此時需要斷開BC那么直接將D指向A即可
    if (ws &gt; 0) {
        do {
            //首先做 do
            //獲取當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)的前置節(jié)點(diǎn)pred.prev
            //因為當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)pred的status大于0 說明當(dāng)前節(jié)點(diǎn)是被取消的 需要斷開
            //繼續(xù)往前找當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)的前置節(jié)點(diǎn)pred.prev
            //如果當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)的前置節(jié)點(diǎn)pred.prev的status還是大于0 說明也是被取消的
            //那么繼續(xù)往前找
            //一直到將當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)以及當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)之前被取消的節(jié)點(diǎn)都斷開
            //看看代碼是怎么做的
            //獲取當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)的前置節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn) 
            pred = pred.prev;
            //然后將當(dāng)前節(jié)點(diǎn)的前置指針指向當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)的前置節(jié)點(diǎn)
            node.prev = pred;
        } while (pred.waitStatus &gt; 0);
        //斷開的是當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn) 以及 當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)之前被取消的節(jié)點(diǎn)
        //從后往前斷開的
        pred.next = node;
    } else {
        //將當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)即頭結(jié)點(diǎn)改為-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    //第一次進(jìn)入返回false 因為是死循環(huán) 等第二次進(jìn)入的時候
    // 符合 ws == Node.SIGNAL 會返回true
    return false;
}

parkAndCheckInterrupt:park并檢查中斷

//代碼塊10
private final boolean parkAndCheckInterrupt() {
    //此處park
    LockSupport.park(this);
    //當(dāng)前線程被unpark喚醒時,當(dāng)前方法返回true或者false都要重新進(jìn)入死循環(huán)然后陷入阻塞,一直等獲取到鎖才能被打斷
    //不同的是
    //parkAndCheckInterrupt:返回true
    //會執(zhí)行interrupted = true; 
    //再次進(jìn)入死循環(huán),再次執(zhí)行shouldParkAfterFailedAcquire(p, node) &amp;&amp; parkAndCheckInterrupt()然后阻塞        
    //parkAndCheckInterrupt:返回false
    //會直接進(jìn)入死循環(huán)再次執(zhí)行shouldParkAfterFailedAcquire(p, node)&amp;&amp;parkAndCheckInterrupt()然后阻塞   
    //這里為什么要調(diào)用interrupted() 而不是isInterrupted() ?
    //interrupted會重置打斷標(biāo)記為false 而isInterrupted只是返回打斷標(biāo)記
    //當(dāng)park的線程在被調(diào)用interrupt方法時,會把中斷狀態(tài)設(shè)置為true。
    //然后park方法會去判斷中斷狀態(tài),如果為true,就直接返回,然后往下繼續(xù)執(zhí)行,如果為false繼續(xù)阻塞
    return Thread.interrupted();
}

注意 是否需要進(jìn)入park阻塞是由當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的waitStatus == Node.SIGNAL 來決定,而不是本節(jié)點(diǎn)的waitStatus 決定。

目前頭結(jié)點(diǎn)的waitStatus==Node.SIGNAL==-1,線程2所在節(jié)點(diǎn)的waitStatus==0。

判斷前置節(jié)點(diǎn)waitstatus是否是SIGNAL即-1阻塞等待喚醒,如果前置節(jié)點(diǎn)是-1那么自己也進(jìn)入阻塞
如果前置節(jié)點(diǎn)的waitstatus是大于0,說明節(jié)點(diǎn)已經(jīng)被取消,遞歸斷開這些節(jié)點(diǎn)返回false。
繼續(xù)進(jìn)入死循環(huán)判斷前置節(jié)點(diǎn)狀態(tài),此時前置節(jié)點(diǎn)的waitstatus是0,將當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)即頭結(jié)點(diǎn)改為-1,返回false。
繼續(xù)進(jìn)入死循環(huán)判斷前置節(jié)點(diǎn)狀態(tài),此時前置節(jié)點(diǎn)的waitstatus是-1,那么自己也進(jìn)入阻塞返回true

加鎖是從當(dāng)前節(jié)點(diǎn)往前找,如果前置節(jié)點(diǎn)已經(jīng)被取消,那么繼續(xù)往前找,找到一個沒有被取消的節(jié)點(diǎn)為止。

解鎖是從當(dāng)前節(jié)點(diǎn)往后找,如果后置節(jié)點(diǎn)已經(jīng)被取消,那么繼續(xù)從后往前找,找到一個沒有被取消的節(jié)點(diǎn)為止。

cancelAcquire:出隊

出隊是有條件的:必須拋出異常。只有在打斷模式下才會拋出異常進(jìn)入finally調(diào)用cancelAcquire方法出隊。

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        //省略代碼
    }finally {
       /**
        這里的failed 什么時候變成true的?
        默認(rèn)的failed=true
        在死循環(huán)一直都是true?。?!因為一直沒有獲取鎖成功!!
        除非是獲取到了鎖才被賦值為false
         1.try代碼塊拋出異常
        ***/
        if (failed)
            cancelAcquire(node);
    }
private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;
        node.thread = null;
        Node pred = node.prev;
        while (pred.waitStatus &gt; 0)
            node.prev = pred = pred.prev;
        Node predNext = pred.next;
        node.waitStatus = Node.CANCELLED;
        if (node == tail &amp;&amp; compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            int ws;
            if (pred != head &amp;&amp;
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws &lt;= 0 &amp;&amp; compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &amp;&amp;
                pred.thread != null) {
                Node next = node.next;
                if (next != null &amp;&amp; next.waitStatus &lt;= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }
            node.next = node; // help GC
        }
    }

獲取鎖或者阻塞過程中,線程宕掉(系統(tǒng)異?;蚴謩觡ill線程) 。

則會進(jìn)入到acquireQueued的finally代碼里,并判斷failed是否為true,若為true則執(zhí)行cancelAcquire方法放棄獲取鎖。

我們一般都說這個方法是用來中斷線程的,那么這個中斷應(yīng)該怎么理解呢? 就是說把當(dāng)前正在執(zhí)行的線程中斷掉,不讓它繼續(xù)往下執(zhí)行嗎?

其實,不然。 此處,說的中斷僅僅是給線程設(shè)置一個中斷的標(biāo)識(設(shè)置為true),線程還是會繼續(xù)往下執(zhí)行的。而線程怎么停止,則需要由我們自己去處理。 一會兒會用代碼來說明這個。

下面的示例代碼說明當(dāng)1個線程在park狀態(tài)下被interrupt()方法打斷或者被stop,會從之前阻塞的代碼處喚醒并繼續(xù)往下執(zhí)行代碼,而不是我們想象的直接跳出代碼。

//示例代碼1
public static void main(String[] args) {
        Thread thread = new Thread(() -&gt; {
            try {
               while(true){
                System.out.println("start");
                LockSupport.park();
                System.out.println("park");
               }
            } finally {
                System.out.println("end");
            }
        }, "t2");
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    	//此處可以用 thread.stop(); 不推薦
        thread.interrupt();
    }

但是有1個問題,為什么跳不出來循環(huán)呢?

原來當(dāng)調(diào)用interrupt方法時,會把中斷狀態(tài)設(shè)置為true,然后park方法會去判斷中斷狀態(tài),如果為true,就直接返回,然后往下繼續(xù)執(zhí)行,并不會拋出異常。

注意,這里并不會清除中斷標(biāo)志。

參考: http://www.dbjr.com.cn/article/276160.htm

此時我們想到使用Thread.interrupted();方法重置打斷標(biāo)記為false

//示例代碼2 
static volatile Boolean flag = false;
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                while(true){
                    System.out.println("start");
                    LockSupport.park();
                    System.out.println("park");
                    if (flag){
                        Thread.interrupted();
                    }
                }
            } finally {
                System.out.println("end");
            }
        }, "t2");
        thread.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //此處可以用 thread.stop(); 不推薦
        thread.interrupt();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag=true;
    }

發(fā)現(xiàn)上面的代碼還是跳不出循環(huán),而是被park阻塞。這個時候我們嘗試使用拋出異常。

//示例代碼3 
static volatile Boolean flag = false;
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                while(true){
                    System.out.println("start");
                    LockSupport.park();
                    System.out.println("park");
                    if (flag){
                        throw new RuntimeException();
                    }
                }
            } finally {
                System.out.println("end");
            }
        }, "t2");
        thread.start();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //此處可以用 thread.stop(); 不推薦
        thread.interrupt();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag=true;
    }

拋出異常成功終止循環(huán)并執(zhí)行了finally。

其實上面的示例2就是不可打斷模式的原理,示例2是可打斷模式的原理。

非公平鎖不可打斷解鎖源碼

unlock

// 解鎖實現(xiàn)
public void unlock() {
	sync.release(1);
}

release

// AQS 繼承過來的方法, 方便閱讀, 放在此處
public final boolean release(int arg) {
    // 如果所有的鎖釋放成功即state=0
    if (tryRelease(arg)) {
        // 隊列頭節(jié)點(diǎn) 
        Node h = head;
        // 頭結(jié)點(diǎn)不為null 且 waitStatus不等于0 才需要喚醒頭結(jié)點(diǎn)的后置節(jié)點(diǎn)
        // h != null 說明有等待隊列
        // h.waitStatus != 0 說明頭結(jié)點(diǎn)后面有節(jié)點(diǎn)在等待鎖
        // 假設(shè)頭結(jié)點(diǎn)的下一個節(jié)點(diǎn)還沒來得及修改h.waitStatus= -1 會有問題嗎?
        // 不會 因為如果h.waitStatus=0,此時頭結(jié)點(diǎn)的下一個節(jié)點(diǎn)還會再嘗試一次獲取鎖 
        // 因為鎖在這里已經(jīng)被釋放 所以頭結(jié)點(diǎn)的下一個節(jié)點(diǎn)必定能獲取到鎖
        if (h != null && h.waitStatus != 0) {
            // h是隊列頭節(jié)點(diǎn) 
            // unpark AQS 中等待的線程, 進(jìn)入 ㈡
            unparkSuccessor(h);
        }
        return true;
    }
    return false;
}

tryRelease

// ㈠ Sync 繼承過來的方法, 方便閱讀, 放在此處
protected final boolean tryRelease(int releases) {
    // state--
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 支持鎖重入,  state 減為 0, 表明所有的鎖都釋放成功
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    //設(shè)置state為c,c不一定等于0 
    setState(c);
    //返回鎖標(biāo)志位
    return free;
}

unparkSuccessor

// ㈡ AQS 繼承過來的方法, 方便閱讀, 放在此處
//node是頭結(jié)點(diǎn)
private void unparkSuccessor(Node node) {
    // 此處的node節(jié)點(diǎn)為頭結(jié)點(diǎn)
    // 如果頭節(jié)點(diǎn)的狀態(tài)小于0 嘗試重置頭節(jié)點(diǎn)的狀態(tài)為0
 	//改為0的意義在于:在下面的代碼中:頭結(jié)點(diǎn)的下一個節(jié)點(diǎn)被喚醒時會再次嘗試加鎖
    //在shouldParkAfterFailedAcquire 方法中有1個判斷
    //if (ws == Node.SIGNAL) { return true; }
    //返回true代表獲取鎖失敗進(jìn)入parkAndCheckInterrupt方法阻塞
    //這里改為0以后 那么頭結(jié)點(diǎn)的下一個節(jié)點(diǎn)會在被unpark的時候再一次嘗試加鎖
    //如果不改為0 那么頭結(jié)點(diǎn)的下一個節(jié)點(diǎn)會直接進(jìn)入死循環(huán)被park 陷入了死循環(huán)無解了。
    int ws = node.waitStatus;
    if (ws < 0) {
        //配合喚醒線程 再一次嘗試加鎖
        //配合喚醒線程 再一次嘗試加鎖
        //配合喚醒線程 再一次嘗試加鎖
        compareAndSetWaitStatus(node, ws, 0);
    }
    //獲取頭結(jié)點(diǎn)的下一個節(jié)點(diǎn)
    Node s = node.next;
    //node是頭節(jié)點(diǎn)
    //如果頭結(jié)點(diǎn)的后置節(jié)點(diǎn)為空或被取消
    //那么從隊列的末尾從后往前找,找到最前面一個需要unpark的節(jié)點(diǎn)
    //如果頭結(jié)點(diǎn)的后置節(jié)點(diǎn)不為空且沒被取消
    //那么就喚醒頭節(jié)點(diǎn)的下一個節(jié)點(diǎn)
    //這里也是非公平的體現(xiàn)
    if (s == null || s.waitStatus > 0) {
        s = null;
        //循環(huán)遍歷從 AQS 隊列從隊列的末尾從后往前找,找到最前面一個需要unpark的節(jié)點(diǎn)
        //注意這里做了判斷t不等于null且t不等于頭結(jié)點(diǎn)且t.waitStatus <= 0
        //也就是找到的節(jié)點(diǎn)必定是有效的
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0){
                s = t;
            }
    }
    //喚醒頭結(jié)點(diǎn)的下一個節(jié)點(diǎn) 或者 從后往前找到的第1個t.waitStatus<= 0的節(jié)點(diǎn)
    if (s != null)
        //喚醒線程 配合 compareAndSetWaitStatus(node, ws, 0); 再一次嘗試加鎖
        //喚醒線程 配合 compareAndSetWaitStatus(node, ws, 0); 再一次嘗試加鎖
        //喚醒線程 配合 compareAndSetWaitStatus(node, ws, 0); 再一次嘗試加鎖
        LockSupport.unpark(s.thread);
}
}

非公平鎖可重入源碼

getExclusiveOwnerThread

    static final class NonfairSync extends Sync {
		// Sync 繼承過來的方法, 方便閱讀, 放在此處
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
			// 如果已經(jīng)獲得了鎖, 線程還是當(dāng)前線程, 表示發(fā)生了鎖重入
            else if (current == getExclusiveOwnerThread()) {
			// state++
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
        // Sync 繼承過來的方法, 方便閱讀, 放在此處
        protected final boolean tryRelease(int releases) {
		// state--
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
		// 支持鎖重入, 只有 state 減為 0, 才釋放成功
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
    }

以上就是java開發(fā)非公平鎖不可打斷源碼示例解析的詳細(xì)內(nèi)容,更多關(guān)于java非公平鎖不可打斷的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java文件對話框過濾特定文件類型示例

    java文件對話框過濾特定文件類型示例

    文件作為存儲數(shù)據(jù)的單元,會根據(jù)數(shù)據(jù)類型產(chǎn)生很多分類,也就是所謂的文件類型。在對數(shù)據(jù)文件進(jìn)行操作時,常常需要根據(jù)不同的文件類型來作不同的處理。本實例實現(xiàn)的是讀取文件夾指定類型的文件并顯示到表格控件中
    2014-02-02
  • 淺談SpringBoot內(nèi)嵌Tomcat的實現(xiàn)原理解析

    淺談SpringBoot內(nèi)嵌Tomcat的實現(xiàn)原理解析

    這篇文章主要介紹了淺談SpringBoot內(nèi)嵌Tomcat的實現(xiàn)原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • SpringMVC和rabbitmq集成的使用案例

    SpringMVC和rabbitmq集成的使用案例

    這篇文章主要介紹了SpringMVC和rabbitmq集成的使用案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • java?-jar命令及SpringBoot通過java?-jav啟動項目的過程

    java?-jar命令及SpringBoot通過java?-jav啟動項目的過程

    本篇文章將為大家講述關(guān)于 SpringBoot 項目工程完成后,是如何通過 java-jar 命令來啟動的,以及介紹 java-jar 命令的詳細(xì)內(nèi)容,對SpringBoot java?-jav啟動過程感興趣的朋友跟隨小編一起看看吧
    2023-05-05
  • 一文詳解Spring事務(wù)的實現(xiàn)與本質(zhì)

    一文詳解Spring事務(wù)的實現(xiàn)與本質(zhì)

    這篇文章主要介紹了Spring中事務(wù)的兩種實現(xiàn)方式:聲明式事務(wù)、編程式事務(wù)以及他們的本質(zhì)。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-04-04
  • 基于Java實現(xiàn)PDF文本旋轉(zhuǎn)傾斜

    基于Java實現(xiàn)PDF文本旋轉(zhuǎn)傾斜

    這篇文章主要介紹了基于Java實現(xiàn)PDF文本旋轉(zhuǎn)傾斜,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-05-05
  • java連接HBase,連接不上報錯can not resolve問題及解決

    java連接HBase,連接不上報錯can not resolve問題及解決

    這篇文章主要介紹了java連接HBase,連接不上報錯can not resolve問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 解讀controller層,service層,mapper層,entity層的作用與聯(lián)系

    解讀controller層,service層,mapper層,entity層的作用與聯(lián)系

    這篇文章主要介紹了關(guān)于controller層,service層,mapper層,entity層的作用與聯(lián)系,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • SpringBoot 項目使用hutool 工具進(jìn)行 http 接口調(diào)用的處理方法

    SpringBoot 項目使用hutool 工具進(jìn)行 http 接口調(diào)用的處理方

    在實際的開發(fā)過程中一個互聯(lián)網(wǎng)的項目來說 ,有可能會涉及到調(diào)用外部接口的實際業(yè)務(wù)場景,下面通過本文給大家介紹SpringBoot 項目 使用hutool 工具進(jìn)行 http 接口調(diào)用的處理方法,需要的朋友可以參考下
    2022-06-06
  • SpringCloud之動態(tài)刷新、重試、服務(wù)化的實現(xiàn)

    SpringCloud之動態(tài)刷新、重試、服務(wù)化的實現(xiàn)

    這篇文章主要介紹了SpringCloud 之動態(tài)刷新、重試、服務(wù)化的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10

最新評論