線程的狀態和常用操作


一、線程的狀態

       每個 Java 程序都有一個缺省的主線程,對於 Java 應用程序,主線程是 main()方法執行的線索;對於 Applet 程序,主線程是指揮瀏覽器加載並執行 Java Applet 程序的線索。要想實現多線程,必須在主線程中創建新的線程對象。任何線程一般具有五種狀態,即創建、就緒、運行、阻塞、終止。

1、新生狀態

       在程序中用構造方法(new操作符)創建一個新線程時,如new Thread(r),該線程就是創建狀態,此時它已經有了相應的內存空間和其它資源,但是還沒有開始執行。

2、就緒狀態

       新建線程對象后,調用該線程的 start()方法就可以啟動線程。當線程啟動時,線程進入就緒狀態(runnable)由於還沒有分配CPU,線程將進入線程隊列排隊,等待 CPU 服務,這表明它已經具備了運行條件。當系統挑選一個等待執行的Thread對象后,它就會從等待執行狀態進入執行狀態。系統挑選的動作稱之為“CPU調度"。一旦獲得CPU線程就進入運行狀態並自動調用自己的run方法。

3、運行狀態

       當就緒狀態的線程被調用並獲得處理器資源時,線程就進入了運行狀態。此時,自動調用該線程對象的 run()方法。 run()方法定義了該線程的操作和功能。運行狀態中的線程執行自己的run方法中代碼。直到調用其他方法或者發生阻塞而終止。

4、阻塞狀態

       一個正在執行的線程在某些特殊情況下,如被人為掛起或需要執行耗時的輸入輸出操作時,將讓出 CPU 並暫時中止自己的執行,進入堵塞狀態。在可執行狀態下,如果調用 sleep()、 suspend()、 wait()等方法,線程都將進入堵塞狀態。堵塞時,線程不能進入排隊隊列,只有當引起堵塞的原因被消除后,線程轉入就緒狀態。重新到就緒隊列中排隊等待,這時被CPU調度選中后會從原來停止的位置開始繼續執行。

      記住:阻塞被消除后是回到就緒狀態,不是運行狀態。

5、死亡狀態

      線程調用 stop()方法、destory()方法或 run()方法執行結束后,線程即處於死亡狀態。處於死亡狀態的線程不具有繼續運行的能力。

      不推薦使用stop()方法【會產生異常】  destory()方法【destory是強制終止,不會釋放鎖】

      推薦使用boolen標識來停止線程,如下方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class TestThraed implements Runnable{
     private boolean flag = true ; //線程使用標識
     @Override
     public void run() {
         while (flag) {
             for ( int i = 0 ; i < 10 ; i++) {
                 System.out.println( "TestThread在運行" +i);
             }
         }
     }
     //停止線程
     public void stop(){
         //如果是extends Thread方式實現多線程。不能使用stop方法名。因為Thread類中對stop修飾為final不可重寫
         this .flag = false ;
     }
}


可以通過getState()方法來獲取線程當前的狀態:NEW 、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED

線程的狀態經典圖:


分析上圖:

1、線路1:新生-->就緒(Runnable)-->運行(Running)-->sleep或者join造成阻塞-->回到就緒(Runnable)-->運行(Running)-->死亡


2、線路2:新生-->就緒(Runnable)-->運行(Running)-->遇到synchronized,需要等待鎖的釋放。釋放完成后-->回到就緒(Runnable)-->運行(Running)-->死亡


3、線路3:新生-->就緒(Runnable)-->運行(Running)-->遇到wait造成的等待需要喚醒notify。醒了后-->回到就緒(Runnable)-->運行(Running)-->死亡



二、線程的一些常用操作

1
2
3
4
5
6
7
8
9
10
//取得線程的名字
Thread t = Thread.currentThread();
String name = t.getName();
//設置線程的名字
SetNameThreadDemo tt = new SetNameThreadDemo(); //繼承Thread或者實現Runnable接口的線程類
tt.setName( "test thread" );
//判斷線程是否啟動
調用start()方法之前t.isAlive() = false
t.start();
調用start()方法之后t.isAlive() = true

線程的合並:join

線程的合並是指將某一個線程A在調用A.join()方法合並到正在運行的另一個線程B中,此時線程B處於阻塞狀態需要等到線程A執行完畢后才開始線程B的繼續執行,代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class JoinDemo {
     public static void main(String[] args) throws InterruptedException {
         TestThread t = new TestThread();
         Thread t1 = new Thread(t);
         t1.start();
         for ( int i = 0 ; i < 100 ; i++) {
             /**
              * 當main線程中的i等於50的時候,就把t1線程合並到main線程中執行。此時main線程是處於阻塞狀態
              * 直到t1線程執行完成后,main才開始繼續執行
              */
             if ( 50 ==i) {
                 t1.join();
             }
             System.out.println( "main.." +i);
         }  
     }
}
class TestThread implements Runnable{
     @Override
     public void run() {
         for ( int i = 0 ; i < 100 ; i++) {
             System.out.println( "join.." +i);
         }  
     }
}

join的執行結果如下:

1
2
3
4
5
6
7
8
9
10
main.. 0
.....省略.......
main.. 17
join.. 0
join.. 1
.....省略.......
main.. 49   //當main的i=50的時候就把t1合並到main線程中,直到t1線程執行完成后,才開始執行main線程。
join.. 5 - join.. 99
main.. 50
main.. 51 - main.. 99


線程的暫停:yield

該暫停方法暫停的時候不一定就暫停了,取決於CPU,假如剛暫停CPU調度又調到了該線程那就又啟動了.....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class YieldDemo {
     public static void main(String[] args) throws InterruptedException {
         TestThread1 t = new TestThread1();
         Thread t1 = new Thread(t);
         t1.start();
         for ( int i = 0 ; i < 100 ; i++) {
             //當main線程中的i是20的倍數時,就暫停main線程
             if (i% 20 == 0 ) {
                 Thread.yield(); //yield寫在哪個線程體中,就暫停哪個線程。這里是在main里,就暫停main線程
                 System.out.println( "main線程暫停" );
             }
             System.out.println( "main.." +i);
         }  
     }
}
class TestThread1 implements Runnable{
     @Override
     public void run() {
         for ( int i = 0 ; i < 100 ; i++) {
             System.out.println( "join.." +i);
         }  
     }
}

jield的執行結果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
main線程暫停
main.. 0
main.. 19
main線程暫停
main.. 20
main.. 39
main線程暫停
main.. 40
main.. 59
main線程暫停
main.. 79
main線程暫停
main.. 80
main.. 84
join.. 0
join.. 1


線程的(睡眠)暫停:sleep  休眠不釋放鎖【抱着鎖睡覺

實例:10秒倒計時,當前線程每睡1秒就打印一個數字

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) throws InterruptedException {
     int num = 10 ;
     while ( true ) {
         System.out.println(num--);
         Thread.sleep( 1000 );
         if (num<= 0 ) {
             break ;             
         }
     }
}


守護線程(線程的后台運行)thread.setDaemon(true);

線程的后台運行

 1、對 Java 程序來說,只要還有一個前台線程在運行,這個進程就不會結束,如果一個進程中只有后台線程在運行,這個進程就會結束。

 2、如果某個線程對象在啟動(調用 start()方法)之前調用了 setDaemon(true)方法,這個線程就變成了后台線程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ThreadDaemon {
     public static void main(String[] args) {
         ThreadTest t = new ThreadTest();
         Thread thread = new Thread(t);
         thread.setDaemon( true ); //設置后台運行
         thread.start();
     }
}
class ThreadTest implements Runnable{
     @Override
     public void run() {
         while ( true ) {
             System.out.println(Thread.currentThread().getName()+ " is running" );
         }
     }
}


三、總結

1、造成線程阻塞的方法?

阻塞線程的方法:join、yield、sleep 和Object的wait()方法

2、Java的守護進程(后台進程)?

設置線程為后台進程運行:setDaemon(true) 如果一個進程中只有后台線程在運行,這個進程就會結束。

3、造成線程阻塞后,線程回到哪個狀態了?

通過join、yield、sleep造成線程阻塞后是回到了就緒狀態

3、哪些狀態之后是回到就緒狀態?

 a)通過join、yield、sleep造成線程阻塞后是回到了就緒狀態

 b)遇到synchronized后

 c)遇到Object的等待wait方法后

4、sleep會釋放鎖嗎?

 sleep不會釋放鎖【它會抱着鎖睡覺】

5、線程都有哪些狀態?具體是怎么運行的?

線程有:創建、就緒、運行、阻塞、終止。5種狀態

1.通過new關鍵字創建后,進入到新生狀態

2.調用start后進入就緒狀態

3.CPU調度到本線程后,本線程開始執行。進入到運行狀態

4.運行中遇到join,yield,sleep造成阻塞,進入阻塞狀態。阻塞完成后,又回到就緒狀態

5.線程正常執行完,或者遇到異常終止后,進入死亡狀態

6、終止線程有哪幾種方法?

 線程調用 stop()方法、destory()方法或 run()方法執行結束后,線程即處於死亡狀態。處於死亡狀態的線程不具有繼續運行的能力。

 推薦使用boolen標識來停止線程









免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM