這篇文章給大家分享的是有關(guān)AbstractQueuedSynchronizer預(yù)熱的示例分析的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧。
成都創(chuàng)新互聯(lián)主營(yíng)安慶網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,重慶APP開(kāi)發(fā)公司,安慶h5微信平臺(tái)小程序開(kāi)發(fā)搭建,安慶網(wǎng)站營(yíng)銷推廣歡迎安慶等地區(qū)企業(yè)咨詢
// 我不確定有多少人卡在這里 // 我是這么理解的 某個(gè)對(duì)象在jvm當(dāng)中 是用一塊數(shù)據(jù)來(lái)描述對(duì)象的所有信息 // 那么問(wèn)題來(lái)了 如果我要設(shè)置某個(gè)對(duì)象的字段 通常的方法 對(duì)象引用.setXXXField(xxx)這個(gè)是通常的方法 // 還有一種比較特別的 unsafe提供的 unsafe.objectFieldOffset獲取某個(gè)字段的偏移量 可以理解為存儲(chǔ)信息的地址 // 獲得了偏移地址之后 就可以使用 unsafe.compareAndSwapObject來(lái)原子的設(shè)置某個(gè)對(duì)象的字段 // 就是說(shuō) 繞過(guò)通用的流程 直接修改相關(guān)數(shù)據(jù)了 順帶而且是原子性的 // 可以理解為玩游戲用外掛直接修改內(nèi)存這種場(chǎng)景 headOffset = unsafe.objectFieldOffset(AbstractQueuedSynchronizer.class.getDeclaredField("head")); unsafe.compareAndSwapObject(this, headOffset, expect, update); tailOffset = unsafe.objectFieldOffset(AbstractQueuedSynchronizer.class.getDeclaredField("tail")); unsafe.compareAndSwapObject(this, tailOffset, expect, update); /** * 獨(dú)占式獲取同步狀態(tài),忽略線程的打斷。 * 獲取同步狀態(tài)的邏輯是由重寫(xiě)的模板方法tryAcquire來(lái)實(shí)現(xiàn)的。 * 如果獲取同步狀態(tài)成功,則方法就直接返回。 * 否則,線程就會(huì)入隊(duì),一直會(huì)處于阻塞或者自旋,直到重復(fù)嘗試tryAcquire成功。 * 該方法就是接口Lock#lock的實(shí)現(xiàn)。 * (從方法的介紹上面理解,就是說(shuō),這個(gè)接口直接的效果就是,獲取同步成功,線程就從這個(gè)方法繼續(xù)執(zhí)行下去,如果不成功; * 那么內(nèi)部會(huì)經(jīng)過(guò)一系列復(fù)雜的邏輯計(jì)算,直接體現(xiàn)就是線程不會(huì)繼續(xù)執(zhí)行下去,就一直處于這個(gè)方法內(nèi)部。不執(zhí)行下去的原因是:線程可能處于自旋或者阻塞。) * @param arg 同步狀態(tài)參數(shù) 透?jìng)鬟M(jìn)tryAcquire并且不響應(yīng)終端或者其他情況(超時(shí)) * * 由兩種判斷邏輯 * 1. tryAcquire(arg) -> 返回 * 2. tryAcquire(arg) -> addWaiter(Node.EXECLUSIVE) -> acquireQueued(lastValue, arg) -> 返回并且可能會(huì)中斷線程 * * addWaiter(Node node) 入隊(duì) * acquireQueued(final Node node, int arg) 自旋或者阻塞 * * 這個(gè)方法就是把整個(gè)流程已經(jīng)寫(xiě)死了,必定會(huì)經(jīng)過(guò)這么幾個(gè)步驟。 * 唯一可以影響該方法中的流程,只能是模板方法tryAcquire,它的返回與否,導(dǎo)致流程的走向。 * 把自旋或者阻塞安排在if的條件語(yǔ)句中 會(huì)令人初步一看會(huì)感覺(jué)非常難受。(大神可以這么用,我們平時(shí)還是少用)。 */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } // 老老實(shí)實(shí)的我,一般會(huì)這么寫(xiě) 見(jiàn)笑見(jiàn)笑 public final void acquire(int arg) { // 嘗試獲取同步狀態(tài) if (tryAcquire(arg)) { return; } else { // 先入隊(duì) 這里會(huì)有一個(gè)死循環(huán) Node newNode = addWaiter(Node.EXCLUSIVE); // 再自旋獲取同步狀態(tài) 或者阻塞 這里也會(huì)有死循環(huán) boolean shouldCurrentThreadInterrupted = acquireQueued(newNode, arg); // 再判斷是否需要線程中斷 if (shouldCurrentThreadInterrupted) { selfInterrupt(); } } } // 接下來(lái)看看這個(gè)模板方法的介紹 /** * 嘗試獨(dú)占式獲取同步狀態(tài)。 * 該方法需要查詢對(duì)象當(dāng)前狀態(tài),判斷同步狀態(tài)是否符合預(yù)期。 * (我的理解就是,需要自己實(shí)現(xiàn)自己的邏輯,判斷自己所要實(shí)現(xiàn)的邏輯是否符合自己的預(yù)期。記住是獨(dú)占模式) * * 該方法經(jīng)常再線程執(zhí)行同步時(shí)被調(diào)用。 * 如果方法返回失敗,那么線程就應(yīng)該入隊(duì)了,即使線程還沒(méi)做好入隊(duì)準(zhǔn)備。 * (這里的意思就是說(shuō),線程在競(jìng)爭(zhēng)鎖之前,最好做好充足的準(zhǔn)備工作,也就是前置邏輯要執(zhí)行完,比如各種初始化判斷。加鎖之后就應(yīng)該是確確實(shí)實(shí)的邏輯操作了,最好不要加完鎖之后,又去判斷各種前置業(yè)務(wù)邏輯操作。這個(gè)就是我理解的大師所要闡述的最佳實(shí)踐。) * 入隊(duì)的線程只能等待別人釋放之后喚醒。 * 一般前置方法就是為了實(shí)現(xiàn)Lock#tryLock這個(gè)。 * * 默認(rèn)實(shí)現(xiàn)式UnsupportedOperationException異常。 * * @param arg 請(qǐng)求參數(shù)。 * 一般這個(gè)值是方法唯一的參數(shù),或者保存于條件等待中。 * 所以不建議為這個(gè)值賦予更多其他含義。 * (我認(rèn)為這里的意思是,這個(gè)值不要和業(yè)務(wù)中的某個(gè)條件或者流程掛鉤,讓值單純的標(biāo)識(shí)同步狀態(tài)就好了。) * * @return true加鎖成功。 * @throws IllegalMonitorStateException 如果獲取同步時(shí)發(fā)現(xiàn)同步器處于一個(gè)不正確的狀態(tài)時(shí), * 那么就必須拋出這個(gè)異常,目的時(shí)為了同步器邏輯正確。 * (我的理解,同步器狀態(tài)很重要,必須嚴(yán)肅對(duì)待,因?yàn)橐坏┠硞€(gè)過(guò)程狀態(tài)不正確,后續(xù)的業(yè)務(wù)邏輯可能會(huì)發(fā)生各種不可知的結(jié)果,并且,debug起來(lái)非常麻煩,因?yàn)闃I(yè)務(wù)邏輯可能正確,原因是同步狀態(tài)的出錯(cuò)。這種是很隱晦的。也就是說(shuō),一旦碰到IllegalMonitorStateException,個(gè)人認(rèn)為最好中斷運(yùn)行,排錯(cuò)。即使開(kāi)發(fā)者認(rèn)為這個(gè)錯(cuò)誤不重要。你都已經(jīng)自己實(shí)現(xiàn)鎖的邏輯了,任何一點(diǎn)小的邏輯失誤,都會(huì)造成不可預(yù)估的結(jié)果。千里之堤毀于蟻穴啊。) * @throws UnsupportedOperationException 如果獨(dú)占模式不支持拋異常 */ protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); } // 入隊(duì)操作 /** * 創(chuàng)建隊(duì)列,并且把當(dāng)前線程包裝一下,指定某個(gè)節(jié)點(diǎn)模式,入隊(duì)。 * * @param mode Node.EXCLUSIVE 獨(dú)占, Node.SHARED 共享 * @return 新的節(jié)點(diǎn) */ private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // 先嘗試直接隊(duì)尾添加 如果不行在進(jìn)行完整的入隊(duì)操作 Try the fast path of enq; backup to full enq on failure Node pred = tail; // 隊(duì)尾有兩種情況 // 1 null 表示隊(duì)列還沒(méi)有初始化 初始化在enq(node)中 // 2 != null 表示隊(duì)列初始化了 那么嘗試快速添加隊(duì)尾這個(gè)操作 我認(rèn)為就是優(yōu)化操作了 // (老老實(shí)實(shí)的我,一般并不會(huì)這么寫(xiě),因?yàn)槲冶容^穩(wěn)妥。) // (其實(shí)優(yōu)化操作,理論上來(lái)說(shuō),可以不用的。) // compareAndSetTail()這個(gè)原子性的操作 防止并發(fā) // 并發(fā)操作的特點(diǎn)就是,隨時(shí)隨地都可能發(fā)生幾個(gè)線程同時(shí)執(zhí)行,所以,并發(fā)點(diǎn),盡量條件簡(jiǎn)單點(diǎn),如果業(yè)務(wù)條件夠復(fù)雜,一定要拆,而且要分優(yōu)先級(jí)的。不然,動(dòng)態(tài)變化的條件加上鎖,噩夢(mèng)。 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { // 入隊(duì)操作只需要建立一個(gè)尾鏈接就可以 pred.next = node; return node; // 注意 這里返回的是新的節(jié)點(diǎn) } } enq(node); // 這里方法返回的是節(jié)點(diǎn)前置的節(jié)點(diǎn) 但是沒(méi)有使用 在喚醒流程中會(huì)復(fù)用這個(gè)方法 return node; } // 完整的入隊(duì)流程邏輯 /** * 入隊(duì)操作,一定要先初始化隊(duì)列。 * (死循環(huán)確保一定會(huì)入隊(duì)成功,我對(duì)死循環(huán)的理解是,單線程不要用死循環(huán),多線程可以適量的用,主線程不要用,非要用時(shí)情愿開(kāi)個(gè)線程計(jì)算,等它計(jì)算結(jié)束再拿那個(gè)結(jié)果也可以??偨Y(jié)起來(lái),能不用就不用,即使要用,千萬(wàn)別忘記了,自己在干什么。建議在自己精力最旺盛的時(shí)候,寫(xiě)帶有死循環(huán)的邏輯。) * @param node 入隊(duì)節(jié)點(diǎn) * @return 返回前置節(jié)點(diǎn) */ private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // 隊(duì)列初始化 // 原子性的設(shè)置頭 這里注意這個(gè)head節(jié)點(diǎn) 這個(gè)head指向的node是一個(gè)空的node,里面沒(méi)有node的關(guān)鍵數(shù)據(jù)的 if (compareAndSetHead(new Node())) tail = head; } else { // 雙向隊(duì)列 嘗試把當(dāng)前節(jié)點(diǎn)的頭設(shè)置為原本隊(duì)尾那個(gè) 只要下面的cas隊(duì)列設(shè)置好那就操作成功 不行再循環(huán)再來(lái) node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } } /** * 設(shè)置隊(duì)列首節(jié)點(diǎn) (因?yàn)槭请p向,隊(duì)首的前驅(qū)是null,這個(gè)null是為了釋放節(jié)點(diǎn)的。) * 該方法僅僅只被同步器獲取。 * null的目的是為了GC也為了不必要的信號(hào)釋放遍歷。 * * @param node 設(shè)置隊(duì)首 */ private void setHead(Node node) { head = node; node.thread = null; node.prev = null; } // 自旋 /** * 獨(dú)占不響應(yīng)中斷模式的線程獲取同步方法。 * 條件等待也使用該方法。 * * @param node 節(jié)點(diǎn) * @param arg 獲取同步參數(shù) * @return true 如果等待時(shí)線程被打斷 */ final boolean acquireQueued(final Node node, int arg) { // 獲取同步狀態(tài)是否失敗 // 默認(rèn)標(biāo)記值是成功的 boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); // 節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)就是頭節(jié)點(diǎn) // 說(shuō)明前面的節(jié)點(diǎn),要么持有同步狀態(tài)在進(jìn)行業(yè)務(wù)邏輯操作,要么就已經(jīng)釋放鎖了。這種情況下,獲取同步器機(jī)會(huì)就很大。 // 再次嘗試獲取同步狀態(tài) if (p == head && tryAcquire(arg)) { // 這里已經(jīng)說(shuō)明當(dāng)前節(jié)點(diǎn)已經(jīng)獲得了同步狀態(tài) 也就是說(shuō)當(dāng)前線程也獲得執(zhí)行業(yè)務(wù)邏輯的機(jī)會(huì)了 // 設(shè)置頭節(jié)點(diǎn)很有技巧 設(shè)置完之后 頭已經(jīng)是一個(gè)虛擬的節(jié)點(diǎn)了 setHead(node); p.next = null; // help GC failed = false; // 這里其實(shí)個(gè)人認(rèn)為是不需要設(shè)置了 除了習(xí)慣原因 我不知道還有什么特別的意思?因?yàn)榉祷氐臅r(shí)候是表示線程是否被打斷了標(biāo)記 return interrupted; } // 獲取失敗判斷線程是否需要阻塞 // 阻塞之后又要檢查線程是否需要中斷 // if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; // 線程已經(jīng)被打斷 } } finally { if (failed) cancelAcquire(node); } } /** * 當(dāng)一個(gè)節(jié)點(diǎn)獲取同狀態(tài)失敗時(shí),檢查并且更新它的狀態(tài)。 * 返回true,那么線程需要被阻塞。 * 在所有的獲取同步循環(huán)中,這個(gè)是最重要的信號(hào)控制。 * 前置條件是前置節(jié)點(diǎn)確切的是節(jié)點(diǎn)的前置節(jié)點(diǎn)。 * * @param pred 帶有狀態(tài)的前驅(qū)節(jié)點(diǎn) * @param node 節(jié)點(diǎn) * @return true 線程被阻塞 */ private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) /* * 前驅(qū)節(jié)點(diǎn)已經(jīng)處于等待其他線程釋放同步狀態(tài)而將它喚醒。 * 那么當(dāng)前節(jié)點(diǎn)應(yīng)該能夠安全的被阻塞。 */ return true; if (ws > 0) { /* * 前驅(qū)節(jié)點(diǎn)已經(jīng)是取消狀態(tài)。 * 跳過(guò)前驅(qū)節(jié)點(diǎn)在嘗試。 */ do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* * 等待狀態(tài)必須是0或者是傳播狀態(tài)(-3)。 * 僅需要一個(gè)信號(hào),而并不需要阻塞。(應(yīng)該是共享模式下的邏輯。) * 調(diào)用者需要重新確保當(dāng)前線程在阻塞之前是否需要獲取同步狀態(tài)。 */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } /** * 阻塞當(dāng)前線程?;謴?fù)后檢測(cè)線程是否被中斷了。 * * @return true} if interrupted */ private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
感謝各位的閱讀!關(guān)于“AbstractQueuedSynchronizer預(yù)熱的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
網(wǎng)站題目:AbstractQueuedSynchronizer預(yù)熱的示例分析
標(biāo)題路徑:http://chinadenli.net/article16/gphcdg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制開(kāi)發(fā)、網(wǎng)站維護(hù)、網(wǎng)站制作、標(biāo)簽優(yōu)化、定制網(wǎng)站、微信公眾號(hào)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)