標籤:
線程的生命週期
由可以看出:
線程的狀態轉換是線程式控制制的基礎。線程狀態總的可分為五大狀態:分別是生、死、可運行、運行、等待/阻塞。
各個狀態細節不再贅述,具體請看http://blog.csdn.net/u011225629/article/details/46288995此篇博文
此篇博文主要講述各個狀態的執行個體!
一、建立和就緒狀態
此處不做細緻的講解,在上一篇博文中已經講解過了!
package com.haixu.thread2;public class InvokeRun extends Thread{/* * 建立就緒狀態練習 * 通過繼承Thread類來建立線程 * */private int i;public void run(){for(; i<5; i++){//直接調用run()方法是,Thread的this.getName()返回的是該對象的名字//使用Thread.currentThread().getName()調用當前線程擷取線程名字System.out.println(Thread.currentThread().getName() + " " + i);}}public static void main(String[] args) {for(int i=0; i<4; i++){//使用Thread.currentThread().getName()調用當前線程擷取線程名字System.out.println(Thread.currentThread().getName() + " " + i);if(i == 3){new InvokeRun().start();//直接調用線程對象的run()方法new InvokeRun().run();new InvokeRun().start();}}}}
二、阻止線程執行
堵塞狀態是前述四種狀態中最有趣的,值得我們作進一步的探討。線程被堵塞可能是由下述五方面的原因造成的:
(1) 調用sleep(毫秒數),使線程進入“睡眠”狀態。在規定的時間內,這個線程是不會啟動並執行。
(2) 用suspend()暫停了線程的執行。除非線程收到resume()訊息,否則不會返回“可運行”狀態。
(3) 用wait()暫停了線程的執行。除非線程收到nofify()或者notifyAll()訊息,否則不會變成“可運行”(是的,這看起來同原因2非常相象,但有一個明顯的區別是我們馬上要揭示的)。
(4) 線程正在等候一些IO(輸入輸出)操作完成。
(5) 線程試圖調用另一個對象的“同步”方法,但那個對象處於鎖定狀態,暫時無法使用。
解決上面的堵塞,讓線程重新進入就緒狀態:
1、調用sleep()方法的的線程經過的指定的時間。
2、線程調用的阻塞式IO方法返回、
3、線程成功的獲得了試圖取得的同步監視器。
4、線程正在等待某個通知,其他線程發出了一個通知。
5、處於掛起狀態的線程被調用了resume()恢複方法。
注意:1、線程睡眠是協助所有線程獲得運行機會的最好方法。2、線程睡眠到期自動蘇醒,並返回到可運行狀態,不是運行狀態。sleep()中指定的時間是線程不會啟動並執行最短時間。因此,sleep()方法不能保證該線程睡眠到期後就開始執行。3、sleep()是靜態方法,只能控制當前正在啟動並執行線程。
/** * 一個計數器,計數到100,在每個數字之間暫停1秒,每隔10個數字輸出一個字串 * * @author leizhimin 2008-9-14 9:53:49 */ public class MyThread extends Thread { public void run() { for (int i = 0; i < 100; i++) { if ((i) % 10 == 0) { System.out.println("-------" + i); } System.out.print(i); try { Thread.sleep(1); System.out.print(" 線程睡眠1毫秒!\n"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { new MyThread().start(); } }
1、睡眠Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的線程休眠(暫停執行),以“減慢線程”。當線程睡眠時,它入睡在某個地方,在蘇醒之前不會返回到可運行狀態。當睡眠時間到期,則返回到可運行狀態。 線程睡眠的原因:線程執行太快,或者需要強制進入下一輪,因為Java規範不保證合理的輪換。 睡眠的實現:調用靜態方法。 try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
} 睡眠的位置:為了讓其他線程有機會執行,可以將Thread.sleep()的調用放線程run()之內。這樣才能保證該線程執行過程中會睡眠。
package com.haixu.thread2;import java.util.Date;public class SleepTest { public static void main(String[] args) throws Exception{ for( int i=0; i<5; i++){ System.out.println("當前的時間:" + new Date()); Thread.sleep(1000); } }}
運行結果:
當前的時間:Sun May 31 17:59:25 CST 2015當前的時間:Sun May 31 17:59:26 CST 2015當前的時間:Sun May 31 17:59:27 CST 2015當前的時間:Sun May 31 17:59:28 CST 2015當前的時間:Sun May 31 17:59:29 CST 2015
2、線程的優先順序和線程讓步yield()
線程的讓步是通過Thread.
yield()來實現的。yield()方法的作用是:暫停當前正在執行的線程對象,並執行其他線程。 要理解yield(),必須瞭解線程的優先順序的概念。線程總是存在優先順序,優先順序範圍在1~10之間。JVM線程發送器是基於優先順序的搶先調度機制。在大多數情況下,當前啟動並執行線程優先順序將大於或等於線程池中任何線程的優先順序。但這僅僅是大多數情況。 注意:當設計多線程應用程式的時候,一定不要依賴於線程的優先順序。因為線程調度優先順序操作是沒有保障的,只能把線程優先順序作用作為一種提高程式效率的方法,但是要保證程式不依賴這種操作。 當線程池中線程都具有相同的優先順序,發送器的JVM實現自由選擇它喜歡的線程。這時候發送器的操作有兩種可能:一是選擇一個線程運行,直到它阻塞或者運行完成為止。二是時間分區,為池內的每個線程提供均等的運行機會。 設定線程的優先順序:線程預設的優先順序是建立它的執行線程的優先順序。可以通過setPriority(int newPriority)更改線程的優先順序。例如: Thread t = new MyThread();
t.setPriority(8);
t.start();線程優先順序為1~10之間的正整數,JVM從不會改變一個線程的優先順序。然而,1~10之間的值是沒有保證的。一些JVM可能不能識別10個不同的值,而將這些優先順序進行每兩個或多個合并,變成少於10個的優先順序,則兩個或多個優先順序的線程可能被映射為一個優先順序。 線程預設優先順序是5,Thread類中有三個常量,定義線程優先順序範圍:static int MAX_PRIORITY
線程可以具有的最高優先順序。
static int MIN_PRIORITY
線程可以具有的最低優先順序。
static int NORM_PRIORITY
分配給線程的預設優先順序。 3、Thread.yield()方法 Thread.yield()方法作用是:暫停當前正在執行的線程對象,並執行其他線程。yield()應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先順序的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優先順序的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程發送器再次選中。結論:yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。
package com.haixu.thread2;/* * 使用Yield對線程讓步,是正在執行的線程停止 * */public class YieldTest extends Thread{public YieldTest(String name){super(name);}//定義run()作為執行體public void run(){for( int i=0; i<6; i++){System.out.println(getName() + " " + i);//當i=4時線程做出讓步if(i==4){Thread.yield();}}}public static void main(String[] args) {//啟動兩個並發線程YieldTest yt = new YieldTest("進階");yt.setPriority(Thread.MAX_PRIORITY);yt.start();//將此線程設定成低許可權YieldTest yt1 = new YieldTest("低級");yt1.setPriority(Thread.MIN_PRIORITY);yt1.start();}}運行結果:
進階 0進階 1進階 2進階 3低級 0低級 1低級 2低級 3進階 4進階 5低級 4低級 5
4、join()方法 Thread的非靜態方法join()讓一個線程B“加入”到另外一個線程A的尾部。在A執行完畢之前,B不能工作。例如: Thread t = new MyThread();
t.start();
t.join();另外,join()方法還有帶逾時限制的重載版本。 例如t.join(5000);則讓線程等待5000毫秒,如果超過這個時間,則停止等待,變為可運行狀態。 線程的加入join()對線程棧導致的結果是線程棧發生了變化,當然這些變化都是瞬時的
package com.haixu.thread2;/* * 當調用其他線程的join方法時,調用線程被阻塞 * 直到被join方法加入的線程執行完為止 * */public class JoinTest extends Thread{//提供有參數的構造器,用於設定線程的名稱 public JoinTest(String name){ super(name); } public void run(){ for(int i = 0; i<6; i++){ System.out.println(getName() + " " + i); } } public static void main(String[] args) throws InterruptedException { //啟動子線程 new JoinTest("新線程").start(); for(int i=0; i< 5; i++){ if(i == 2){ JoinTest jt = new JoinTest("被Join的線程"); jt.start(); //main線程調用了jt線程的join方法,main線程 //必須等jt之行結束才會向下執行 jt.join(); } System.out.println(Thread.currentThread().getName() + " " + i); } }}
執行結果:
main 0main 1新線程 0新線程 1新線程 2新線程 3新線程 4新線程 5被Join的線程 0被Join的線程 1被Join的線程 2被Join的線程 3被Join的線程 4被Join的線程 5main 2main 3main 4
瘋狂Java學習筆記(64)------------線程轉換