【69期】面試官:對并發(fā)熟悉嗎?談?wù)劸€程間的協(xié)作(wait/notify/sleep/yield/join)
閱讀本文大概需要 8.5 分鐘。
來自:www.cnblogs.com/paddix/p/5381958.html
一、線程的狀態(tài)
New:新建狀態(tài),當(dāng)線程創(chuàng)建完成時為新建狀態(tài),即new Thread(…),還沒有調(diào)用start方法時,線程處于新建狀態(tài)。
Runnable:就緒狀態(tài),當(dāng)調(diào)用線程的的start方法后,線程進入就緒狀態(tài),等待CPU資源。處于就緒狀態(tài)的線程由Java運行時系統(tǒng)的線程調(diào)度程序(thread scheduler)來調(diào)度。
Running:運行狀態(tài),就緒狀態(tài)的線程獲取到CPU執(zhí)行權(quán)以后進入運行狀態(tài),開始執(zhí)行run方法。
Blocked:阻塞狀態(tài),線程沒有執(zhí)行完,由于某種原因(如,I/O操作等)讓出CPU執(zhí)行權(quán),自身進入阻塞狀態(tài)。
Dead:死亡狀態(tài),線程執(zhí)行完成或者執(zhí)行過程中出現(xiàn)異常,線程就會進入死亡狀態(tài)。

二、wait/notify/notifyAll方法的使用
1、wait方法:

wait()方法的作用是將當(dāng)前運行的線程掛起(即讓其進入阻塞狀態(tài)),直到notify或notifyAll方法來喚醒線程.
wait(long timeout),該方法與wait()方法類似,唯一的區(qū)別就是在指定時間內(nèi),如果沒有notify或notifAll方法的喚醒,也會自動喚醒。
至于wait(long timeout,long nanos),本意在于更精確的控制調(diào)度時間,不過從目前版本來看,該方法貌似沒有完整的實現(xiàn)該功能,其源碼(JDK1.8)如下:
public?final?void?wait(long?timeout,?int?nanos)?throws?InterruptedException?{
????????if?(timeout?0)?{
????????????throw?new?IllegalArgumentException("timeout?value?is?negative");
????????}
????????if?(nanos?0?||?nanos?>?999999)?{
????????????throw?new?IllegalArgumentException(
????????????????????????????????"nanosecond?timeout?value?out?of?range");
????????}
????????if?(nanos?>=?500000?||?(nanos?!=?0?&&?timeout?==?0))?{
????????????timeout++;
????????}
????????wait(timeout);
????}
package?com.paddx.test.concurrent;
public?class?WaitTest?{
????public?void?testWait(){
????????System.out.println("Start-----");
????????try?{
????????????wait(1000);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println("End-------");
????}
????public?static?void?main(String[]?args)?{
????????final?WaitTest?test?=?new?WaitTest();
????????new?Thread(new?Runnable()?{
????????????@Override
????????????public?void?run()?{
????????????????test.testWait();
????????????}
????????}).start();
????}
}
Start-----
Exception?in?thread?"Thread-0"?java.lang.IllegalMonitorStateException
????at?java.lang.Object.wait(Native?Method)
????at?com.paddx.test.concurrent.WaitTest.testWait(WaitTest.java:8)
????at?com.paddx.test.concurrent.WaitTest$1.run(WaitTest.java:20)
????at?java.lang.Thread.run(Thread.java:745)
Thrown to indicate that a thread has attempted to wait on an object's monitor or to notify other threads waiting on an object's monitor without owning the specified monitor.
package?com.paddx.test.concurrent;
public?class?WaitTest?{
????public?synchronized?void?testWait(){//增加Synchronized關(guān)鍵字
????????System.out.println("Start-----");
????????try?{
????????????wait(1000);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println("End-------");
????}
????public?static?void?main(String[]?args)?{
????????final?WaitTest?test?=?new?WaitTest();
????????new?Thread(new?Runnable()?{
????????????@Override
????????????public?void?run()?{
????????????????test.testWait();
????????????}
????????}).start();
????}
}
Start-----
End-------
2、notify/notifyAll方法

package?com.paddx.test.concurrent;
public?class?NotifyTest?{
????public?synchronized?void?testWait(){
????????System.out.println(Thread.currentThread().getName()?+"?Start-----");
????????try?{
????????????wait(0);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println(Thread.currentThread().getName()?+"?End-------");
????}
????public?static?void?main(String[]?args)?throws?InterruptedException?{
????????final?NotifyTest?test?=?new?NotifyTest();
????????for(int?i=0;i<5;i++)?{
????????????new?Thread(new?Runnable()?{
????????????????@Override
????????????????public?void?run()?{
????????????????????test.testWait();
????????????????}
????????????}).start();
????????}
????????synchronized?(test)?{
????????????test.notify();
????????}
????????Thread.sleep(3000);
????????System.out.println("-----------分割線-------------");
????????synchronized?(test)?{
????????????test.notifyAll();
????????}
????}
}
Thread-0?Start-----
Thread-1?Start-----
Thread-2?Start-----
Thread-3?Start-----
Thread-4?Start-----
Thread-0?End-------
-----------分割線-------------
Thread-4?End-------
Thread-3?End-------
Thread-2?End-------
Thread-1?End-------
線程需要被喚醒(超時喚醒或調(diào)用notify/notifyll)。
線程喚醒后需要競爭到鎖(monitor)。
三、sleep/yield/join方法解析
1、sleep
package?com.paddx.test.concurrent;
public?class?SleepTest?{
????public?synchronized?void?sleepMethod(){
????????System.out.println("Sleep?start-----");
????????try?{
????????????Thread.sleep(1000);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println("Sleep?end-----");
????}
????public?synchronized?void?waitMethod(){
????????System.out.println("Wait?start-----");
????????synchronized?(this){
????????????try?{
????????????????wait(1000);
????????????}?catch?(InterruptedException?e)?{
????????????????e.printStackTrace();
????????????}
????????}
????????System.out.println("Wait?end-----");
????}
????public?static?void?main(String[]?args)?{
????????final?SleepTest?test1?=?new?SleepTest();
????????for(int?i?=?0;i<3;i++){
????????????new?Thread(new?Runnable()?{
????????????????@Override
????????????????public?void?run()?{
????????????????????test1.sleepMethod();
????????????????}
????????????}).start();
????????}
????????try?{
????????????Thread.sleep(10000);//暫停十秒,等上面程序執(zhí)行完成
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????System.out.println("-----分割線-----");
????????final?SleepTest?test2?=?new?SleepTest();
????????for(int?i?=?0;i<3;i++){
????????????new?Thread(new?Runnable()?{
????????????????@Override
????????????????public?void?run()?{
????????????????????test2.waitMethod();
????????????????}
????????????}).start();
????????}
????}
}
Sleep?start-----
Sleep?end-----
Sleep?start-----
Sleep?end-----
Sleep?start-----
Sleep?end-----
-----分割線-----
Wait?start-----
Wait?start-----
Wait?start-----
Wait?end-----
Wait?end-----
Wait?end-----
2、yield方法
package?com.paddx.test.concurrent;
public?class?YieldTest?implements?Runnable?{
????@Override
????public?void?run()?{
????????try?{
????????????Thread.sleep(100);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????for(int?i=0;i<5;i++){
????????????System.out.println(Thread.currentThread().getName()?+?":?"?+?i);
????????????Thread.yield();
????????}
????}
????public?static?void?main(String[]?args)?{
????????YieldTest?runn?=?new?YieldTest();
????????Thread?t1?=?new?Thread(runn,"FirstThread");
????????Thread?t2?=?new?Thread(runn,"SecondThread");
????????t1.start();
????????t2.start();
????}
}
FirstThread:?0
SecondThread:?0
FirstThread:?1
SecondThread:?1
FirstThread:?2
SecondThread:?2
FirstThread:?3
SecondThread:?3
FirstThread:?4
SecondThread:?4
/**
?????*?A?hint?to?the?scheduler?that?the?current?thread?is?willing?to?yield
?????*?its?current?use?of?a?processor.?The?scheduler?is?free?to?ignore?this
?????*?hint.
?????*
?????*??Yield?is?a?heuristic?attempt?to?improve?relative?progression
?????*?between?threads?that?would?otherwise?over-utilise?a?CPU.?Its?use
?????*?should?be?combined?with?detailed?profiling?and?benchmarking?to
?????*?ensure?that?it?actually?has?the?desired?effect.
?????*
?????*??It?is?rarely?appropriate?to?use?this?method.?It?may?be?useful
?????*?for?debugging?or?testing?purposes,?where?it?may?help?to?reproduce
?????*?bugs?due?to?race?conditions.?It?may?also?be?useful?when?designing
?????*?concurrency?control?constructs?such?as?the?ones?in?the
?????*?{@link?java.util.concurrent.locks}?package.
*/
調(diào)度器可能會忽略該方法。
使用的時候要仔細分析和測試,確保能達到預(yù)期的效果。
很少有場景要用到該方法,主要使用的地方是調(diào)試和測試。
3、join方法

public?final?void?join()?throws?InterruptedException?{
????????join(0);
????}
?public?final?synchronized?void?join(long?millis)
????throws?InterruptedException?{
????????long?base?=?System.currentTimeMillis();
????????long?now?=?0;
????????if?(millis?0)?{
????????????throw?new?IllegalArgumentException("timeout?value?is?negative");
????????}
????????if?(millis?==?0)?{
????????????while?(isAlive())?{
????????????????wait(0);
????????????}
????????}?else?{
????????????while?(isAlive())?{
????????????????long?delay?=?millis?-?now;
????????????????if?(delay?<=?0)?{
????????????????????break;
????????????????}
????????????????wait(delay);
????????????????now?=?System.currentTimeMillis()?-?base;
????????????}
????????}
????}
public?final?synchronized?void?join(long?millis,?int?nanos)
????throws?InterruptedException?{
????????if?(millis?0)?{
????????????throw?new?IllegalArgumentException("timeout?value?is?negative");
????????}
????????if?(nanos?0?||?nanos?>?999999)?{
????????????throw?new?IllegalArgumentException(
????????????????????????????????"nanosecond?timeout?value?out?of?range");
????????}
????????if?(nanos?>=?500000?||?(nanos?!=?0?&&?millis?==?0))?{
????????????millis++;
????????}
????????join(millis);
????}
package?com.paddx.test.concurrent;
public?class?JoinTest?implements?Runnable{
????@Override
????public?void?run()?{
????????try?{
????????????System.out.println(Thread.currentThread().getName()?+?"?start-----");
????????????Thread.sleep(1000);
????????????System.out.println(Thread.currentThread().getName()?+?"?end------");
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????}
????public?static?void?main(String[]?args)?{
????????for?(int?i=0;i<5;i++)?{
????????????Thread?test?=?new?Thread(new?JoinTest());
????????????test.start();
????????}
????????System.out.println("Finished~~~");
????}
}
Thread-0?start-----
Thread-1?start-----
Thread-2?start-----
Thread-3?start-----
Finished~~~
Thread-4?start-----
Thread-2?end------
Thread-4?end------
Thread-1?end------
Thread-0?end------
Thread-3?end------
package?com.paddx.test.concurrent;
public?class?JoinTest?implements?Runnable{
????@Override
????public?void?run()?{
????????try?{
????????????System.out.println(Thread.currentThread().getName()?+?"?start-----");
????????????Thread.sleep(1000);
????????????System.out.println(Thread.currentThread().getName()?+?"?end------");
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????}
????public?static?void?main(String[]?args)?{
????????for?(int?i=0;i<5;i++)?{
????????????Thread?test?=?new?Thread(new?JoinTest());
????????????test.start();
????????????try?{
????????????????test.join();?//調(diào)用join方法
????????????}?catch?(InterruptedException?e)?{
????????????????e.printStackTrace();
????????????}
????????}
????????System.out.println("Finished~~~");
????}
}
Thread-0?start-----
Thread-0?end------
Thread-1?start-----
Thread-1?end------
Thread-2?start-----
Thread-2?end------
Thread-3?start-----
Thread-3?end------
Thread-4?start-----
Thread-4?end------
Finished~~~
四、總結(jié)
推薦閱讀:
【68期】面試官:對并發(fā)熟悉嗎?說說Synchronized及實現(xiàn)原理
【67期】談?wù)凜oncurrentHashMap是如何保證線程安全的?
微信掃描二維碼,關(guān)注我的公眾號
朕已閱?

