1. 程式人生 > >java多執行緒之六種狀態

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