java開(kāi)發(fā)非公平鎖不可打斷源碼示例解析
非公平鎖不可打斷調(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分鐘,因?yàn)樾枰驍帱c(diǎn),線程2再去獲取鎖。
非公平鎖不可打斷加鎖源碼
lock
public void lock() { sync.lock(); }
final void lock() { //首先用線程1使用 cas 嘗試將 state 從 0 改為 1,如果成功表示獲得了鎖 //因?yàn)榫€程1獲取到了鎖state現(xiàn)在等于1,所以此時(shí)線程2獲取鎖失敗。 //線程2執(zhí)行acquire(1); //非公平的體現(xiàn):上來(lái)就加鎖 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做尾部入隊(duì)操作 //線程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 那么會(huì)執(zhí)行selfInterrupt自我打斷 //selfInterrupt方法只有1句代碼:Thread.currentThread().interrupt(); selfInterrupt(); } }
tryAcquire:嘗試加鎖&判斷鎖重入
//acquires=1 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
//此時(shí) 線程2累計(jì)嘗試2次加鎖 final boolean nonfairTryAcquire(int acquires) { //acquires=1 final Thread current = Thread.currentThread(); int c = getState(); //如果線程1已經(jīng)釋放鎖 此時(shí)c==0滿足 會(huì)再次使用cas嘗試加鎖 //這里線程1仍然持有鎖 條件不滿足 if (c == 0) { // 嘗試用 cas 獲得, 這里體現(xiàn)了非公平性: 不去檢查 AQS 隊(duì)列 // 非公平鎖可以提高并發(fā)度,但是會(huì)導(dǎo)致饑餓,可以使用超時(shí)時(shí)間解決饑餓 // 線程切換的開(kāi)銷,其實(shí)就是非公平鎖效率高于公平鎖的原因 // 因?yàn)榉枪芥i減少了線程掛起的幾率,后來(lái)的線程有一定幾率節(jié)省被掛起的開(kāi)銷 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); //代表加鎖成功 return true; } } // 判斷是否鎖重入 else if (current == getExclusiveOwnerThread()) { //使用原來(lái)的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:加入同步隊(duì)列
addWaiter方法的第一個(gè)參數(shù)是mode,Node.EXCLUSIVE是個(gè)null。
static final Node EXCLUSIVE = null;
acquireQueued方法的第一個(gè)參數(shù)是node,其實(shí)就是線程2所在的Node節(jié)點(diǎn)。第二個(gè)參數(shù)是1:代表了本次state加鎖成功累加的數(shù)量。
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
由于acquireQueued方法的參數(shù)是addWaiter方法的返回值,因此先看addWaiter方法
//node是Node.EXCLUSIVE 默認(rèn)是null //enq方法創(chuàng)建的隊(duì)列如下:頭結(jié)點(diǎn)->尾結(jié)點(diǎn)(線程2所在節(jié)點(diǎn)) //后續(xù)的節(jié)點(diǎn)都在addWaiter方法中入隊(duì),不再進(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是等待隊(duì)列中的指針 /*** Node(Thread thread, Node mode) { this.nextWaiter = mode; this.thread = thread; } ***/ Node node = new Node(Thread.currentThread(), mode); //獲取同步隊(duì)列的尾部節(jié)點(diǎn) Node pred = tail; //此時(shí)同步隊(duì)列的tail是null,因?yàn)榈侥壳盀橹共](méi)有執(zhí)行過(guò)enq方法 //如果tail不為null:使用cas嘗試將Node對(duì)象插入隊(duì)列尾部,作為新的尾結(jié)點(diǎn) if (pred != null) { //將當(dāng)前node節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)指向原tail節(jié)點(diǎn) node.prev = pred; //將當(dāng)前node節(jié)點(diǎn)作為新的尾節(jié)點(diǎn) if (compareAndSetTail(pred, node)) { //原來(lái)的尾節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn) pred.next = node; return node; } } //因?yàn)閠ail節(jié)點(diǎn)是null 嘗試將Node加入隊(duì)列 enq(node); //返回線程2節(jié)點(diǎn) return node; }
//下面解釋中的當(dāng)前節(jié)點(diǎn)指的是Thread-2所在的節(jié)點(diǎn) //enq相當(dāng)于是初始化頭尾結(jié)點(diǎn)和第一個(gè)入隊(duì)的節(jié)點(diǎn) //只有第1個(gè)入隊(duì)的節(jié)點(diǎn)才會(huì)進(jìn)入該方法 //后續(xù)的線程都會(huì)直接執(zhí)行enq(node)之前的代碼加入尾節(jié)點(diǎn) //enq方法構(gòu)造了1個(gè)雙向隊(duì)列:頭結(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àn)闆](méi)有對(duì)應(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) 此時(shí)頭和尾是同一個(gè)節(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):此時(shí)頭結(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)那么這個(gè)鏈表是雙向鏈表 t.next = node; //循環(huán)結(jié)束 return t; } } } }
//node是 線程2的節(jié)點(diǎn) //arg = 1 //node.predecessor():獲取當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn) //node.predecessor()和node.prev不同的是: //node.prev如果是null不會(huì)拋出異常 //node.predecessor()中如果 node.prev是 null 會(huì)拋出異常 //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)開(kāi)始 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í)行 //此時(shí)累計(jì)嘗試3次 if (p == head && 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和原來(lái)的頭節(jié)點(diǎn)斷開(kāi) //因?yàn)楫?dāng)前節(jié)點(diǎn)獲取鎖成功,意味著線程1已經(jīng)釋放鎖,此時(shí)需要和代表線程1的原來(lái)的頭結(jié)點(diǎn)斷開(kāi)。 setHead(node); //將原來(lái)的頭節(jié)點(diǎn)斷開(kāi)和當(dāng)前節(jié)點(diǎn)的連接 相當(dāng)于原來(lái)的節(jié)點(diǎn)出隊(duì) p.next = null; // help GC failed = false; //注意這是在死循環(huán)里 //如果interrupted返回的是true 將會(huì)執(zhí)行 selfInterrupt(); 自我中斷 // if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){selfInterrupt();} //即:獲取鎖阻塞的過(guò)程中被打斷,也要重新進(jìn)入死循環(huán)一直等到獲取鎖才能執(zhí)行打斷,這就是不可打斷。 //可打斷是指在等待鎖的過(guò)程中,其它線程可以用interrupt方法終止等待,synchronized鎖是不可打斷的。 //我們要想在等鎖的過(guò)程中被打斷,就要使用lockInterruptibly()方法對(duì)lock對(duì)象加鎖,而不是lock()方法。 return interrupted; } //第一次進(jìn)入shouldParkAfterFailedAcquire //將當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)即頭結(jié)點(diǎn)改為-1 返回false (累計(jì)嘗試4次) //如果當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)以及更前面的節(jié)點(diǎn)有取消的節(jié)點(diǎn) //要斷開(kāi)這些節(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時(shí) //會(huì)進(jìn)入parkAndCheckInterrupt()方法中,然后會(huì)park當(dāng)前線程 //Thread-2所在node被阻塞,然后等待喚醒,此時(shí)node的waitStatus=0 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()){ //在不可打斷模式中 //線程park在parkAndCheckInterrupt方法里 //如果線程被打斷,parkAndCheckInterrupt方法返回true //執(zhí)行以下代碼 //Interrupted = true //Interrupted = true 代表被阻塞期間打斷過(guò) //然后繼續(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 什么時(shí)候變成true的? 默認(rèn)的failed=true 在死循環(huán)一直都是true?。?!因?yàn)橐恢睕](méi)有獲取鎖成功?。? 除非是獲取到了鎖才被賦值為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){ // 上一個(gè)節(jié)點(diǎn)=-1 都在阻塞, 那么自己也阻塞好了 //返回true代表要park return true; } //如果當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)的waitStatus>0 //說(shuō)明當(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<---C<---D //假如B節(jié)點(diǎn)被取消,此時(shí)需要斷開(kāi)C那么直接將D指向B即可 //A<---B<---C<---D //假如BC節(jié)點(diǎn)被取消,此時(shí)需要斷開(kāi)BC那么直接將D指向A即可 if (ws > 0) { do { //首先做 do //獲取當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)的前置節(jié)點(diǎn)pred.prev //因?yàn)楫?dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)pred的status大于0 說(shuō)明當(dāng)前節(jié)點(diǎn)是被取消的 需要斷開(kāi) //繼續(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 說(shuō)明也是被取消的 //那么繼續(xù)往前找 //一直到將當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)以及當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)之前被取消的節(jié)點(diǎn)都斷開(kāi) //看看代碼是怎么做的 //獲取當(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 > 0); //斷開(kāi)的是當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn) 以及 當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)之前被取消的節(jié)點(diǎn) //從后往前斷開(kāi)的 pred.next = node; } else { //將當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)即頭結(jié)點(diǎn)改為-1 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } //第一次進(jìn)入返回false 因?yàn)槭撬姥h(huán) 等第二次進(jìn)入的時(shí)候 // 符合 ws == Node.SIGNAL 會(huì)返回true return false; }
parkAndCheckInterrupt:park并檢查中斷
//代碼塊10 private final boolean parkAndCheckInterrupt() { //此處park LockSupport.park(this); //當(dāng)前線程被unpark喚醒時(shí),當(dāng)前方法返回true或者false都要重新進(jìn)入死循環(huán)然后陷入阻塞,一直等獲取到鎖才能被打斷 //不同的是 //parkAndCheckInterrupt:返回true //會(huì)執(zhí)行interrupted = true; //再次進(jìn)入死循環(huán),再次執(zhí)行shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()然后阻塞 //parkAndCheckInterrupt:返回false //會(huì)直接進(jìn)入死循環(huán)再次執(zhí)行shouldParkAfterFailedAcquire(p, node)&&parkAndCheckInterrupt()然后阻塞 //這里為什么要調(diào)用interrupted() 而不是isInterrupted() ? //interrupted會(huì)重置打斷標(biāo)記為false 而isInterrupted只是返回打斷標(biāo)記 //當(dāng)park的線程在被調(diào)用interrupt方法時(shí),會(huì)把中斷狀態(tài)設(shè)置為true。 //然后park方法會(huì)去判斷中斷狀態(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 來(lái)決定,而不是本節(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,說(shuō)明節(jié)點(diǎn)已經(jīng)被取消,遞歸斷開(kāi)這些節(jié)點(diǎn)返回false。
繼續(xù)進(jìn)入死循環(huán)判斷前置節(jié)點(diǎn)狀態(tài),此時(shí)前置節(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),此時(shí)前置節(jié)點(diǎn)的waitstatus是-1,那么自己也進(jìn)入阻塞返回true
加鎖是從當(dāng)前節(jié)點(diǎn)往前找,如果前置節(jié)點(diǎn)已經(jīng)被取消,那么繼續(xù)往前找,找到一個(gè)沒(méi)有被取消的節(jié)點(diǎn)為止。
解鎖是從當(dāng)前節(jié)點(diǎn)往后找,如果后置節(jié)點(diǎn)已經(jīng)被取消,那么繼續(xù)從后往前找,找到一個(gè)沒(méi)有被取消的節(jié)點(diǎn)為止。
cancelAcquire:出隊(duì)
出隊(duì)是有條件的:必須拋出異常。只有在打斷模式下才會(huì)拋出異常進(jìn)入finally調(diào)用cancelAcquire方法出隊(duì)。
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { //省略代碼 }finally { /** 這里的failed 什么時(shí)候變成true的? 默認(rèn)的failed=true 在死循環(huán)一直都是true?。。∫?yàn)橐恢睕](méi)有獲取鎖成功?。? 除非是獲取到了鎖才被賦值為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 > 0) node.prev = pred = pred.prev; Node predNext = pred.next; node.waitStatus = Node.CANCELLED; if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null); } else { int ws; if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); } else { unparkSuccessor(node); } node.next = node; // help GC } }
獲取鎖或者阻塞過(guò)程中,線程宕掉(系統(tǒng)異?;蚴謩?dòng)kill線程) 。
則會(huì)進(jìn)入到acquireQueued的finally代碼里,并判斷failed是否為true,若為true則執(zhí)行cancelAcquire方法放棄獲取鎖。
我們一般都說(shuō)這個(gè)方法是用來(lái)中斷線程的,那么這個(gè)中斷應(yīng)該怎么理解呢? 就是說(shuō)把當(dāng)前正在執(zhí)行的線程中斷掉,不讓它繼續(xù)往下執(zhí)行嗎?
其實(shí),不然。 此處,說(shuō)的中斷僅僅是給線程設(shè)置一個(gè)中斷的標(biāo)識(shí)(設(shè)置為true),線程還是會(huì)繼續(xù)往下執(zhí)行的。而線程怎么停止,則需要由我們自己去處理。 一會(huì)兒會(huì)用代碼來(lái)說(shuō)明這個(gè)。
下面的示例代碼說(shuō)明當(dāng)1個(gè)線程在park狀態(tài)下被interrupt()方法打斷或者被stop,會(huì)從之前阻塞的代碼處喚醒并繼續(xù)往下執(zhí)行代碼,而不是我們想象的直接跳出代碼。
//示例代碼1 public static void main(String[] args) { Thread thread = new Thread(() -> { 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個(gè)問(wèn)題,為什么跳不出來(lái)循環(huán)呢?
原來(lái)當(dāng)調(diào)用interrupt方法時(shí),會(huì)把中斷狀態(tài)設(shè)置為true,然后park方法會(huì)去判斷中斷狀態(tài),如果為true,就直接返回,然后往下繼續(xù)執(zhí)行,并不會(huì)拋出異常。
注意,這里并不會(huì)清除中斷標(biāo)志。
參考: http://www.dbjr.com.cn/article/276160.htm
此時(shí)我們想到使用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阻塞。這個(gè)時(shí)候我們嘗試使用拋出異常。
//示例代碼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。
其實(shí)上面的示例2就是不可打斷模式的原理,示例2是可打斷模式的原理。
非公平鎖不可打斷解鎖源碼
unlock
// 解鎖實(shí)現(xiàn) public void unlock() { sync.release(1); }
release
// AQS 繼承過(guò)來(lái)的方法, 方便閱讀, 放在此處 public final boolean release(int arg) { // 如果所有的鎖釋放成功即state=0 if (tryRelease(arg)) { // 隊(duì)列頭節(jié)點(diǎn) Node h = head; // 頭結(jié)點(diǎn)不為null 且 waitStatus不等于0 才需要喚醒頭結(jié)點(diǎn)的后置節(jié)點(diǎn) // h != null 說(shuō)明有等待隊(duì)列 // h.waitStatus != 0 說(shuō)明頭結(jié)點(diǎn)后面有節(jié)點(diǎn)在等待鎖 // 假設(shè)頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)還沒(méi)來(lái)得及修改h.waitStatus= -1 會(huì)有問(wèn)題嗎? // 不會(huì) 因?yàn)槿绻鹔.waitStatus=0,此時(shí)頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)還會(huì)再嘗試一次獲取鎖 // 因?yàn)殒i在這里已經(jīng)被釋放 所以頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)必定能獲取到鎖 if (h != null && h.waitStatus != 0) { // h是隊(duì)列頭節(jié)點(diǎn) // unpark AQS 中等待的線程, 進(jìn)入 ㈡ unparkSuccessor(h); } return true; } return false; }
tryRelease
// ㈠ Sync 繼承過(guò)來(lái)的方法, 方便閱讀, 放在此處 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 繼承過(guò)來(lái)的方法, 方便閱讀, 放在此處 //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)的下一個(gè)節(jié)點(diǎn)被喚醒時(shí)會(huì)再次嘗試加鎖 //在shouldParkAfterFailedAcquire 方法中有1個(gè)判斷 //if (ws == Node.SIGNAL) { return true; } //返回true代表獲取鎖失敗進(jìn)入parkAndCheckInterrupt方法阻塞 //這里改為0以后 那么頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)會(huì)在被unpark的時(shí)候再一次嘗試加鎖 //如果不改為0 那么頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)會(huì)直接進(jìn)入死循環(huán)被park 陷入了死循環(huán)無(wú)解了。 int ws = node.waitStatus; if (ws < 0) { //配合喚醒線程 再一次嘗試加鎖 //配合喚醒線程 再一次嘗試加鎖 //配合喚醒線程 再一次嘗試加鎖 compareAndSetWaitStatus(node, ws, 0); } //獲取頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn) Node s = node.next; //node是頭節(jié)點(diǎn) //如果頭結(jié)點(diǎn)的后置節(jié)點(diǎn)為空或被取消 //那么從隊(duì)列的末尾從后往前找,找到最前面一個(gè)需要unpark的節(jié)點(diǎn) //如果頭結(jié)點(diǎn)的后置節(jié)點(diǎn)不為空且沒(méi)被取消 //那么就喚醒頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn) //這里也是非公平的體現(xiàn) if (s == null || s.waitStatus > 0) { s = null; //循環(huán)遍歷從 AQS 隊(duì)列從隊(duì)列的末尾從后往前找,找到最前面一個(gè)需要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)的下一個(gè)節(jié)點(diǎn) 或者 從后往前找到的第1個(gè)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 繼承過(guò)來(lái)的方法, 方便閱讀, 放在此處 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 繼承過(guò)來(lái)的方法, 方便閱讀, 放在此處 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開(kāi)發(fā)非公平鎖不可打斷源碼示例解析的詳細(xì)內(nèi)容,更多關(guān)于java非公平鎖不可打斷的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談SpringBoot內(nèi)嵌Tomcat的實(shí)現(xiàn)原理解析
這篇文章主要介紹了淺談SpringBoot內(nèi)嵌Tomcat的實(shí)現(xiàn)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12java?-jar命令及SpringBoot通過(guò)java?-jav啟動(dòng)項(xiàng)目的過(guò)程
本篇文章將為大家講述關(guān)于 SpringBoot 項(xiàng)目工程完成后,是如何通過(guò) java-jar 命令來(lái)啟動(dòng)的,以及介紹 java-jar 命令的詳細(xì)內(nèi)容,對(duì)SpringBoot java?-jav啟動(dòng)過(guò)程感興趣的朋友跟隨小編一起看看吧2023-05-05一文詳解Spring事務(wù)的實(shí)現(xiàn)與本質(zhì)
這篇文章主要介紹了Spring中事務(wù)的兩種實(shí)現(xiàn)方式:聲明式事務(wù)、編程式事務(wù)以及他們的本質(zhì)。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-04-04基于Java實(shí)現(xiàn)PDF文本旋轉(zhuǎn)傾斜
這篇文章主要介紹了基于Java實(shí)現(xiàn)PDF文本旋轉(zhuǎn)傾斜,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05java連接HBase,連接不上報(bào)錯(cuò)can not resolve問(wèn)題及解決
這篇文章主要介紹了java連接HBase,連接不上報(bào)錯(cuò)can not resolve問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06解讀controller層,service層,mapper層,entity層的作用與聯(lián)系
這篇文章主要介紹了關(guān)于controller層,service層,mapper層,entity層的作用與聯(lián)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11SpringBoot 項(xiàng)目使用hutool 工具進(jìn)行 http 接口調(diào)用的處理方
在實(shí)際的開(kāi)發(fā)過(guò)程中一個(gè)互聯(lián)網(wǎng)的項(xiàng)目來(lái)說(shuō) ,有可能會(huì)涉及到調(diào)用外部接口的實(shí)際業(yè)務(wù)場(chǎng)景,下面通過(guò)本文給大家介紹SpringBoot 項(xiàng)目 使用hutool 工具進(jìn)行 http 接口調(diào)用的處理方法,需要的朋友可以參考下2022-06-06SpringCloud之動(dòng)態(tài)刷新、重試、服務(wù)化的實(shí)現(xiàn)
這篇文章主要介紹了SpringCloud 之動(dòng)態(tài)刷新、重試、服務(wù)化的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10