新聞中心
這篇文章主要講解了“如何理解LockSupport類中的park等待和unpark喚醒”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何理解LockSupport類中的park等待和unpark喚醒”吧!
創(chuàng)新互聯(lián)長期為超過千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為安定企業(yè)提供專業(yè)的網(wǎng)站建設(shè)、網(wǎng)站制作,安定網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
一、傳統(tǒng)synchronized隱式鎖
package com.lau.javabase.lock.LockSupport; import java.util.concurrent.TimeUnit; /** * 使用LockSupport之前,synchronized傳統(tǒng)方式存在的問題: * 1、wait()和notify()方法不能脫離同步代碼塊(鎖)單獨(dú)使用 * 2、B線程的notify()方法在A線程的wait()之前執(zhí)行的話,A線程將不會被喚醒 */ public class BeforeUseTraditional { public static void main(String[] args) { Object lockObj = new Object(); //線程A new Thread(() -> { // try { // TimeUnit.SECONDS.sleep(3); // } catch (InterruptedException e) { // e.printStackTrace(); // } // synchronized (lockObj){ System.out.println(Thread.currentThread().getName() + " come in..."); try { lockObj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " awakened..."); // } }, "A").start(); // try { // TimeUnit.SECONDS.sleep(3); // } catch (InterruptedException e) { // e.printStackTrace(); // } //線程B喚醒線程A new Thread(() -> { // synchronized (lockObj){ lockObj.notify(); System.out.println(Thread.currentThread().getName() + " notify..."); // } }, "B").start(); } }
輸出:
A come in... Exception in thread "A" Exception in thread "B" java.lang.IllegalMonitorStateException at java.lang.Object.notify(Native Method) at com.lau.javabase.lock.LockSupport.BeforeUseTraditional.lambda$main$1(BeforeUseTraditional.java:42) at java.lang.Thread.run(Thread.java:745) java.lang.IllegalMonitorStateException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:502) at com.lau.javabase.lock.LockSupport.BeforeUseTraditional.lambda$main$0(BeforeUseTraditional.java:25) at java.lang.Thread.run(Thread.java:745) Process finished with exit code 0
結(jié)論:wait()和notify()方法不能脫離同步代碼塊(鎖)單獨(dú)使用
二、ReentrantLock顯示鎖
package com.lau.javabase.lock.LockSupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 使用LockSupport之前,Lock方式存在的問題: * 1、await()和signal()方法不能脫離同步代碼塊(鎖)單獨(dú)使用 * 2、B線程的和signal()方法在A線程的await()之前執(zhí)行的話,A線程將不會被喚醒 */ public class BeforeUseLock { public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); //線程A new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } try { lock.lock(); System.out.println(Thread.currentThread().getName() + " come in..."); try { condition.await(); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " awakened..."); } finally { lock.unlock(); } }, "A").start(); // try { // TimeUnit.SECONDS.sleep(3); // } catch (InterruptedException e) { // e.printStackTrace(); // } //線程B喚醒線程A new Thread(() -> { try{ lock.lock(); condition.signal(); System.out.println(Thread.currentThread().getName() + " notify..."); } finally { lock.unlock(); } }, "B").start(); } }
輸出:
B notify... A come in...
結(jié)論:B線程的和signal()方法在A線程的await()之前執(zhí)行的話,A線程將不會被喚醒
三、LockSupport類
package com.lau.javabase.lock.LockSupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** * 使用LockSupport,不會存在以下問題: * 1、await()和signal()方法不能脫離同步代碼塊(鎖)單獨(dú)使用 * 2、B線程的和signal()方法在A線程的await()之前執(zhí)行的話,A線程將不會被喚醒 */ public class LockSupportTest { public static void main(String[] args) { //線程A Thread threadA = new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " come in..."); LockSupport.park(); System.out.println(Thread.currentThread().getName() + " awakened..."); }, "A"); threadA.start(); // try { // TimeUnit.SECONDS.sleep(3); // } catch (InterruptedException e) { // e.printStackTrace(); // } //線程B喚醒線程A new Thread(() -> { LockSupport.unpark(threadA); System.out.println(Thread.currentThread().getName() + " notify..."); }, "B").start(); } }
輸出:
B notify... A come in... A awakened...
結(jié)論:使用LockSupport,不會存在以上兩個(gè)問題
四、說明
LockSupport是用來創(chuàng)建鎖和其他同步類的基本線程阻塞原語
LockSupport是一個(gè)線程阻塞工具類,所有的方法都是靜態(tài)方法,可以讓線程在任意位置阻塞,阻塞之后也有對應(yīng)的喚醒方法。歸根
結(jié)底,LockSupport調(diào)用的Unsafe中的native代碼。
LockSupport提供park()和unpark()方法實(shí)現(xiàn)阻塞線程和解除線程阻塞的過程
LockSupport和每個(gè)使用它的線程都有一個(gè)許可(permit)關(guān)聯(lián)。permit相當(dāng)于1,0的開關(guān),默認(rèn)是0,
調(diào)用一次unpark就加1變成1,
調(diào)用一次park會消費(fèi)permit,也就是將1變成o,同時(shí)park立即返回。
如再次調(diào)用park會變成阻塞(因?yàn)閜ermit為零了會阻塞在這里,一直到permit變?yōu)?),這時(shí)調(diào)用unpark會把permit置為1。
每個(gè)線程都有一個(gè)相關(guān)的permit, permit最多只有一個(gè),重復(fù)調(diào)用unpark也不會積累憑證。
形象的理解
線程阻塞需要消耗憑證(permit),這個(gè)憑證最多只有1個(gè)。
當(dāng)調(diào)用park方法時(shí)
*如果有憑證,則會直接消耗掉這個(gè)憑證然后正常退出;
*如果無憑證,就必須阻塞等待憑證可用;
而unpark則相反,它會增加一個(gè)憑證,但憑證最多只能有1個(gè),累加無效。
五、擴(kuò)展
1、為什么可以先喚醒線程后阻塞線程?
因?yàn)閡npark獲得了一個(gè)憑證,之后再調(diào)用park方法,就可以名正言順的憑證消費(fèi),故不會阻塞。
2、為什么喚醒兩次后阻塞兩次,但最終結(jié)果還會阻塞線程?
因?yàn)閼{證的數(shù)量最多為1,連續(xù)調(diào)用兩次unpark和調(diào)用一次unpark效果一樣,只會增加一個(gè)憑證;
而調(diào)用兩次park卻需要消費(fèi)兩個(gè)憑證,證不夠,不能放行。
感謝各位的閱讀,以上就是“如何理解LockSupport類中的park等待和unpark喚醒”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對如何理解LockSupport類中的park等待和unpark喚醒這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!
分享題目:如何理解LockSupport類中的park等待和unpark喚醒
網(wǎng)頁網(wǎng)址:http://www.dlmjj.cn/article/ipdidi.html