1.如何實現一個生產者和消費者模型?(鎖、信號量、線程通信、阻塞隊列等) "Java生產者和消費者模型的5種實現方式" 2.如何理解線程的同步和非同步、阻塞和非阻塞? 作者:Yi Lu 鏈接:https://www.zhihu.com/question/19732473/answer/20851256 ...
1.如何實現一個生產者和消費者模型?(鎖、信號量、線程通信、阻塞隊列等)
2.如何理解線程的同步和非同步、阻塞和非阻塞?
作者:Yi Lu
鏈接:https://www.zhihu.com/question/19732473/answer/20851256
來源:知乎
“阻塞”與"非阻塞"與"同步"與“非同步"不能簡單的從字面理解,提供一個從分散式系統角度的回答。
1.同步與非同步
同步和非同步關註的是消息通信機制 (synchronous communication/ asynchronous communication)
所謂同步,就是在發出一個調用時,在沒有得到結果之前,該調用就不返回。但是一旦調用返回,就得到返回值了。
換句話說,就是由調用者主動等待這個調用的結果。
而非同步則是相反,調用*在發出之後,這個調用就直接返回了,所以沒有返回結果。換句話說,當一個非同步過程調用發出後,調用者不會立刻得到結果。而是在調用發出後,被調用者*通過狀態、通知來通知調用者,或通過回調函數處理這個調用。
典型的非同步編程模型比如Node.js
舉個通俗的例子:
你打電話問書店老闆有沒有《分散式系統》這本書,如果是同步通信機制,書店老闆會說,你稍等,”我查一下",然後開始查啊查,等查好了(可能是5秒,也可能是一天)告訴你結果(返回結果)。
而非同步通信機制,書店老闆直接告訴你我查一下啊,查好了打電話給你,然後直接掛電話了(不返回結果)。然後查好了,他會主動打電話給你。在這裡老闆通過“回電”這種方式來回調。
-
阻塞與非阻塞
阻塞和非阻塞關註的是程式在等待調用結果(消息,返回值)時的狀態.
阻塞調用是指調用結果返回之前,當前線程會被掛起。調用線程只有在得到結果之後才會返回。
非阻塞調用指在不能立刻得到結果之前,該調用不會阻塞當前線程。
還是上面的例子,
你打電話問書店老闆有沒有《分散式系統》這本書,你如果是阻塞式調用,你會一直把自己“掛起”,直到得到這本書有沒有的結果,如果是非阻塞式調用,你不管老闆有沒有告訴你,你自己先一邊去玩了, 當然你也要偶爾過幾分鐘check一下老闆有沒有返回結果。
在這裡阻塞與非阻塞與是否同步非同步無關。跟老闆通過什麼方式回答你結果無關。
如果是關心阻塞 IO/ 非同步 IO, 參考 Unix Network Programming View Book
還是2014年寫的以解釋概念為主,主要是同步非同步 阻塞和非阻塞會被用在不同層面上,可能會有不准確的地方,並沒有針對 阻塞 IO/ 非同步 IO 等進行討論,大家可以後續看看這兩個回答:
3.線程池處理任務的流程是怎樣的?
1,首先線程池判斷基本線程池是否已滿(< corePoolSize ?)?沒滿,創建一個工作線程來執行任務。滿了,則進入下個流程。
2,其次線程池判斷工作隊列是否已滿?沒滿,則將新提交的任務存儲在工作隊列里。滿了,則進入下個流程。
3,最後線程池判斷整個線程池是否已滿(< maximumPoolSize ?)?沒滿,則創建一個新的工作線程來執行任務,滿了,則交給飽和策略來處理這個任務。
總結:線程池優先要創建出基本線程池大小(corePoolSize)的線程數量,沒有達到這個數量時,每次提交新任務都會直接創建一個新線程,當達到了基本線程數量後,又有新任務到達,優先放入等待隊列,如果隊列滿了,才去創建新的線程(不能超過線程池的最大數maxmumPoolSize)
4.wait和sleep有什麼不同?
區別1:使用限制
使用 sleep 方法可以讓讓當前線程休眠,時間一到當前線程繼續往下執行,在任何地方都能使用,但需要捕獲 InterruptedException 異常。
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
而使用 wait 方法則必須放在 synchronized 塊裡面,同樣需要捕獲 InterruptedException 異常,並且需要獲取對象的鎖。
synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
而且 wait 還需要額外的方法 notify/ notifyAll 進行喚醒,它們同樣需要放在 synchronized 塊裡面,且獲取對象的鎖。。
synchronized (lock) {
// 隨機喚醒
lock.notify();
// 喚醒全部
lock.notifyAll();
}
當然也可以使用帶時間的 wait(long millis) 方法,時間一到,無需其他線程喚醒,也會重新競爭獲取對象的鎖繼續執行。
區別2:使用場景
sleep 一般用於當前線程休眠,或者輪循暫停操作,wait 則多用於多線程之間的通信。
區別3:所屬類
sleep 是 Thread 類的靜態本地方法,wait 則是 Object 類的本地方法。
java.lang.Thread#sleep
public static native void sleep(long millis) throws InterruptedException;
java.lang.Object#wait
public final native void wait(long timeout) throws InterruptedException;
為什麼要這樣設計呢?
因為 sleep 是讓當前線程休眠,不涉及到對象類,也不需要獲得對象的鎖,所以是線程類的方法。wait 是讓獲得對象鎖的線程實現等待,前提是要楚獲得對象的鎖,所以是類的方法。
區別4:釋放鎖
Object lock = new Object();
synchronized (lock) {
try {
lock.wait(3000L);
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
如上代碼所示,wait 可以釋放當前線程對 lock 對象鎖的持有,而 sleep 則不會。
區別5:線程切換
sleep 會讓出 CPU 執行時間且強制上下文切換,而 wait 則不一定,wait 後可能還是有機會重新競爭到鎖繼續執行的。
5.Synchronized和ReentrantLock有什麼不同?各適合什麼場景?
1、ReentrantLock 擁有Synchronized相同的併發性和記憶體語義,此外還多了鎖投票,定時鎖等候和中斷鎖等候
example:線程A和B都要獲取對象O的鎖定,假設A獲取了對象O鎖,B將等待A釋放對O的鎖定,如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長的時間以後,中斷等待,而乾別的事情
ReentrantLock獲取鎖定與三種方式:
-
lock(), 如果獲取了鎖立即返回,如果別的線程持有鎖,當前線程則一直處於休眠狀態,直到獲取鎖
-
tryLock(), 如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false;
-
tryLock(long timeout,TimeUnit unit), 如果獲取了鎖定立即返回true,如果別的線程正持有鎖,會等待參數給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false;
-
lockInterruptibly:如果獲取了鎖定立即返回,如果沒有獲取鎖定,當前線程處於休眠狀態,直到或者鎖定,或者當前線程被別的線程中斷
2、synchronized是在JVM層面上實現的,不但可以通過一些監控工具監控synchronized的鎖定,而且在代碼執行時出現異常,JVM會自動釋放鎖定,但是使用Lock則不行,lock是通過代碼實現的,要保證鎖定一定會被釋放,就必須將unLock()放到finally{}中
3、在資源競爭不是很激烈的情況下,Synchronized的性能要優於ReetrantLock,但是在資源競爭很激烈的情況下,Synchronized的性能會下降幾十倍,但是ReetrantLock的性能能維持常態;
作者:OverLight
鏈接:https://www.jianshu.com/p/4dbacf1cadcf
來源:簡書
6.讀寫鎖適用於什麼場景?ReentrantReadWriteLock是如何實現的?
7.線程之間如何通信?
[JAVA多線程之線程間的通信方式](https://www.cnblogs.com/hapjin/p/5492619.html)