這篇文章主要講解了“如何理解LockSupport類中的park等待和unpark喚醒”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何理解LockSupport類中的park等待和unpark喚醒”吧!
package com.lau.javabase.lock.LockSupport;
import java.util.concurrent.TimeUnit;
/**
* 使用LockSupport之前,synchronized傳統方式存在的問題:
* 1、wait()和notify()方法不能脫離同步代碼塊(鎖)單獨使用
* 2、B線程的notify()方法在A線程的wait()之前執行的話,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
結論:wait()和notify()方法不能脫離同步代碼塊(鎖)單獨使用
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()方法不能脫離同步代碼塊(鎖)單獨使用
* 2、B線程的和signal()方法在A線程的await()之前執行的話,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...
結論:B線程的和signal()方法在A線程的await()之前執行的話,A線程將不會被喚醒
package com.lau.javabase.lock.LockSupport;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
/**
* 使用LockSupport,不會存在以下問題:
* 1、await()和signal()方法不能脫離同步代碼塊(鎖)單獨使用
* 2、B線程的和signal()方法在A線程的await()之前執行的話,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...
結論:使用LockSupport,不會存在以上兩個問題
LockSupport是用來創建鎖和其他同步類的基本線程阻塞原語
LockSupport是一個線程阻塞工具類,所有的方法都是靜態方法,可以讓線程在任意位置阻塞,阻塞之后也有對應的喚醒方法。歸根
結底,LockSupport調用的Unsafe中的native代碼。
LockSupport提供park()和unpark()方法實現阻塞線程和解除線程阻塞的過程
LockSupport和每個使用它的線程都有一個許可(permit)關聯。permit相當于1,0的開關,默認是0,
調用一次unpark就加1變成1,
調用一次park會消費permit,也就是將1變成o,同時park立即返回。
如再次調用park會變成阻塞(因為permit為零了會阻塞在這里,一直到permit變為1),這時調用unpark會把permit置為1。
每個線程都有一個相關的permit, permit最多只有一個,重復調用unpark也不會積累憑證。
形象的理解
線程阻塞需要消耗憑證(permit),這個憑證最多只有1個。
當調用park方法時
*如果有憑證,則會直接消耗掉這個憑證然后正常退出;
*如果無憑證,就必須阻塞等待憑證可用;
而unpark則相反,它會增加一個憑證,但憑證最多只能有1個,累加無效。
1、為什么可以先喚醒線程后阻塞線程?
因為unpark獲得了一個憑證,之后再調用park方法,就可以名正言順的憑證消費,故不會阻塞。
2、為什么喚醒兩次后阻塞兩次,但最終結果還會阻塞線程?
因為憑證的數量最多為1,連續調用兩次unpark和調用一次unpark效果一樣,只會增加一個憑證;
而調用兩次park卻需要消費兩個憑證,證不夠,不能放行。
感謝各位的閱讀,以上就是“如何理解LockSupport類中的park等待和unpark喚醒”的內容了,經過本文的學習后,相信大家對如何理解LockSupport類中的park等待和unpark喚醒這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。