Java多線程-線程狀態、sleep()、yield()、join()

Java線程的狀態轉換


對於具體的幾個狀態解釋,這裏就不寫了,比較容易理解。

對於線程的阻止,考慮一下三個方面,不考慮IO阻塞的情況:

l 睡眠:

l 等待

l 因爲需要一個對象的鎖定而被阻塞。


所謂多線程:

所謂的“併發執行”、“同時”其實都不是真正意義上的“同時”。衆所周知,CPU都有個時鐘頻率,表示每秒中能執行cpu指令的次數。在每個時鐘週期內,CPU實際上只能去執行一條(也有可能多條)指令。操作系統將進程線程進行管理,輪流(沒有固定的順序)分配每個進程很短的一段是時間(不一定是均分),然後在每個線程內部,程序代碼自己處理該進程內部線程的時間分配,多個線程之間相互的切換去執行,這個切換時間也是非常短的。因此多任務、多進程、多線程都是操作系統給人的一種宏觀感受,從微觀角度看,程序的運行是異步執行的

用一句話做總結:雖然操作系統是多線程的,但CPU每一時刻只能做一件事,和人的大腦是一樣的,呵呵。


1、睡眠

Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的線程休眠(暫停執行),以“減慢線程”。當線程睡眠時,它入睡在某個地方,在甦醒之前不會返回到可運行狀態。當睡眠時間到期,則返回到可運行狀態。

線程睡眠的原因:線程執行太快,或者需要強制進入下一輪,因爲Java規範不保證合理的輪換。

 

睡眠的實現:調用靜態方法。

    

[java] view plaincopy
  1. try {  
  2.         Thread.sleep(123);  
  3.     } catch (InterruptedException e) {  
  4.         e.printStackTrace();    
  5.     }  


睡眠的位置:爲了讓其他線程有機會執行,可以將Thread.sleep()的調用放線程run()之內。這樣才能保證該線程執行過程中會睡眠。

注意:

1、線程睡眠是幫助所有線程獲得運行機會的最好方法。

2、線程睡眠到期自動甦醒,並返回到可運行狀態,不是運行狀態。sleep()中指定的時間是線程不會運行的最短時間。因此,sleep()方法不能保證該線程睡眠到期後就開始執行。

3、sleep()是靜態方法,只能控制當前正在運行的線程。


2、線程的優先級和線程讓步yield() 

線程的讓步是通過Thread.yield()來實現的。yield()方法的作用是:暫停當前正在執行的線程對象,並執行其他線程


要理解yield(),必須瞭解線程的優先級的概念。線程總是存在優先級,優先級範圍在1~10之間。JVM線程調度程序是基於優先級的搶先調度機制。在大多數情況下,當前運行的線程優先級將大於或等於線程池中任何線程的優先級。但這僅僅是大多數情況。 


注意:當設計多線程應用程序的時候,一定不要依賴於線程的優先級。因爲線程調度優先級操作是沒有保障的,只能把線程優先級作用作爲一種提高程序效率的方法,但是要保證程序不依賴這種操作。 


線程池中線程都具有相同的優先級,調度程序的JVM實現自由選擇它喜歡的線程。這時候調度程序的操作有兩種可能:一是選擇一個線程運行,直到它阻塞或者運行完成爲止。二是時間分片,爲池內的每個線程提供均等的運行機會。 


設置線程的優先級:線程默認的優先級是創建它的執行線程的優先級。可以通過setPriority(int newPriority)更改線程的優先級。

例如:     

[java] view plaincopy
  1. Thread t = new MyThread();  
  2.      t.setPriority(8);  
  3.      t.start();  

線程優先級爲1~10之間的正整數,JVM從不會改變一個線程的優先級。然而,1~10之間的值是沒有保證的。一些JVM可能不能識別10個不同的值,而將這些優先級進行每兩個或多個合併,變成少於10個的優先級,則兩個或多個優先級的線程可能被映射爲一個優先級。 

線程默認優先級是5Thread類中有三個常量,定義線程優先級範圍: 

static int MAX_PRIORITY  線程可以具有的最高優先級。 
static int MIN_PRIORITY   線程可以具有的最低優先級。 
static int NORM_PRIORITY   分配給線程的默認優先級。 



3Thread.yield()方法 

Thread.yield()方法作用是:暫停當前正在執行的線程對象,並執行其他線程。 

yield()應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相 同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因爲讓步的線程還有可能被線程調度程序再次選中。 

結論:yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。

[java] view plaincopy
  1. /**  
  2. * Java線程:線程的調度-讓步  
  3.  
  4. * @author leizhimin 2009-11-4 9:02:40  
  5. */   
  6. public class Test {   
  7.         public static void main(String[] args) {   
  8.                 Thread t1 = new MyThread1();   
  9.                 Thread t2 = new Thread(new MyRunnable());   
  10.   
  11.                 t2.start();   
  12.                 t1.start();   
  13.         }   
  14. }   
  15.   
  16. class MyThread1 extends Thread {   
  17.         public void run() {   
  18.                 for (int i = 0; i < 10; i++) {   
  19.                         System.out.println("線程1第" + i + "次執行!");   
  20.                 }   
  21.         }   
  22. }   
  23.   
  24. class MyRunnable implements Runnable {   
  25.         public void run() {   
  26.                 for (int i = 0; i < 10; i++) {   
  27.                         System.out.println("線程2第" + i + "次執行!");   
  28.                         Thread.yield();   
  29.                 }   
  30.         }   
  31. }  

4join()方法

Thread的非靜態方法join()讓一個線程B“加入到另外一個線程A的尾部。在A執行完畢之前,B不能工作。例如: 

        Thread t = new MyThread();
        t.start();
        t.join(); 

另外,join()方法還有帶超時限制的重載版本。 例如t.join(5000);則讓線程等待5000毫秒,如果超過這個時間,則停止等待,變爲可運行狀態。 

線程的加入join()線程棧導致的結果是線程棧發生了變化,當然這些變化都是瞬時的。下面給示意圖: 



測試代碼:

public class Main {

	public static class TestThread extends Thread {

		@Override
		public void run() {
			int i = 0;
			long time = System.currentTimeMillis();
			while (i <= 10) {
				while (System.currentTimeMillis() - time < 1000) {
				}
				time = System.currentTimeMillis();
				System.out.println(i);
				i++;
			}
		}
	}

	public static void main(String[] args) throws Exception {
		System.out.println("start");
		TestThread testThread = new TestThread();
		testThread.start();
		testThread.join();
		System.out.println("end");
	}
}

輸出結果:





小結 

到目前位置,介紹了線程離開運行狀態的3種方法: 

1、調用Thread.sleep():使當前線程睡眠至少多少毫秒(儘管它可能在指定的時間之前被中斷)。 

2、調用Thread.yield():不能保障太多事情,儘管通常它會讓當前運行線程回到可運行性狀態,使得有相同優先級的線程有機會執行。 

3、調用join()方法:保證當前線程停止執行,直到該線程所加入的線程完成爲止。然而,如果它加入的線程沒有存活,則當前線程不需要停止。


除了以上三種方式外,還有下面幾種特殊情況可能使線程離開運行狀態: 

1線程run()方法完成。 

2、在對象上調用wait()方法(不是在線程上調用)。 

3線程不能在對象上獲得鎖定,它正試圖運行該對象的方法代碼。 

4線程調度程序可以決定將當前運行狀態移動到可運行狀態,以便讓另一個線程獲得運行機會,而不需要任何理由。



相關引用:

http://wenku.baidu.com/view/07ccd32f647d27284b735148.html

http://blog.csdn.net/liu1028701143/article/details/6754590

http://blog.csdn.net/jimjarry/article/details/6754598


 
發佈了17 篇原創文章 · 獲贊 12 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章