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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章