java多執行緒之六種狀態
一、java執行緒的六種狀態
其中,RUNNABLE狀態包括 【執行中】 和 【就緒】; BLOCKED(阻塞態)狀態只有在【等待進入synchronized方法(塊)】和 【其他Thread呼叫notify()或notifyAll(),但是還未獲得鎖】才會進入;
二、sleep() 、yield()、join()與 wait()/notify()的區別
sleep() 、yield()、join()是Thread的方法,只放棄cpu,但是不放棄鎖 1、Thread.sleep(long millis),一定是當前執行緒呼叫此方法,當前執行緒進入TIMED_WAITING狀態,但不釋放物件鎖,millis後執行緒自動甦醒進入就緒狀態。作用:給其它執行緒執行機會的最佳方式。
2、Thread.yield(),一定是當前執行緒呼叫此方法,當前執行緒放棄獲取的CPU時間片,但不釋放鎖資源,由執行狀態變為就緒狀態,讓OS再次選擇執行緒。作用:讓相同優先順序的執行緒輪流執行,但並不保證一定會輪流執行。實際中無法保證yield()達到讓步目的,因為讓步的執行緒還有可能被執行緒排程程式再次選中。Thread.yield()不會導致阻塞。該方法與sleep()類似,只是不能由使用者指定暫停多長時間。
3、t.join()/t.join(long millis),當前執行緒裡呼叫其它執行緒t的join方法,當前執行緒進入WAITING/TIMED_WAITING狀態,當前執行緒不會釋放已經持有的物件鎖。執行緒t執行完畢或者millis時間到,當前執行緒進入就緒狀態。
wait()是Object的方法,放棄cpu,也放棄鎖
4、obj.wait(),當前執行緒呼叫物件的wait()方法,當前執行緒釋放物件鎖,進入等待佇列。依靠notify()/notifyAll()喚醒或者wait(long timeout) timeout時間到自動喚醒。 5、obj.notify()喚醒在此物件監視器上等待的單個執行緒,選擇是任意性的。notifyAll()喚醒在此物件監視器上等待的所有執行緒。
public class Test { public static void main(String[] args) { new Thread1().start(); new Thread2().start(); } public static class Thread1 extends Thread{ @Override public void run() { synchronized (Test.class){ System.out.println("Thread1 start"); try { /** * 1、wait()和notify()是Object鎖的方法 * 2、wait()會讓出鎖 */ Test.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread1 go on "); } } } public static class Thread2 extends Thread{ @Override public void run() { synchronized (Test.class){ System.out.println("Thread2 start"); /** * 1、notify()呼叫後,該執行緒會等待該同步塊執行完畢才釋放鎖 */ Test.class.notifyAll(); try { /** * 1、sleep()是Thread的方法 * 2、sleep()不讓出鎖,只讓出cpu */ Thread2.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread2 go on"); } } } }
三、LockSupport中的park() 和 unpark()
總結一下,LockSupport比Object的wait/notify有兩大優勢:
①LockSupport不需要在同步程式碼塊裡 ,所以執行緒間也不需要維護一個共享的同步物件了,實現了執行緒間的解耦; 而wait/notify必須在同步塊或同步方法中才能呼叫。
②unpark函式可以先於park呼叫,所以不需要擔心執行緒間的執行的先後順序,而wait必須先於notify。
1、為什麼LockSupport不需要在同步程式碼塊裡而wait()需要?
執行緒A執行一段業務邏輯後呼叫wait阻塞住自己。主執行緒呼叫notify方法喚醒執行緒A,執行緒A然後列印自己執行的結果:
public class TestObjWait {
public static void main(String[] args)throws Exception {
final Object obj = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
try {
synchronized (obj){
obj.wait();
}
}catch (Exception e){
e.printStackTrace();
}
System.out.println(sum);
}
});
A.start();
//睡眠一秒鐘,保證執行緒A已經計算完成,阻塞在wait方法
Thread.sleep(1000);
synchronized (obj){
obj.notify();
}
}
}
使用LockSupport實現:
public class TestObjWait {
public static void main(String[] args)throws Exception {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
int sum = 0;
for(int i=0;i<10;i++){
sum+=i;
}
LockSupport.park();
System.out.println(sum);
}
});
A.start();
//睡眠一秒鐘,保證執行緒A已經計算完成
Thread.sleep(1000);
LockSupport.unpark(A);
}
}
2、為什麼LockSupport不需要擔心unpark函式和park呼叫順序,而Object的wait/notify需要關心?
如果我們將上面程式碼的這一句去掉:
//睡眠一秒鐘,保證執行緒A已經計算完成
Thread.sleep(1000);
那麼,使用wait()和notify()的就會出題,可能A會永遠被掛起,因為主執行緒的notify()先於wait()呼叫了; 但是LockSupport的程式碼還是正確的執行,因為 LockSupport和每個使用它的執行緒都與一個許可(permit)關聯。permit相當於1,0的開關,預設是0; 呼叫unpark就將permit賦值1; 呼叫park時,會判斷permit如果為1,就會將permit賦值0,並且立即返回,如果permit為0,會阻塞在這裡,直到permit變為1