線程的基本操作-狀態、初始化


 

一、線程的狀態:

 

線程有如圖6種狀態,是從JDK源碼中截的圖,有很詳細的注釋。NEW   RUNNABLE  BLOCKED  WAITING  TIMED_WAITING  TERMINATED。

1. New(新建)

    當用new操作符創建一個新線程時,如 new Thread(r), 該線程還沒有開始運行。這意味着它的狀態是new。

    當一個線程處在new狀態,程序還沒有開始運行線程中的代碼。在線程運行之前還有一些基礎工作要做。

2. Runnable(可運行)

   一旦調用start()方法,線程就處於runnable狀態。可以可運行的線程可能正在運行也可能沒有運行,這取決於操作系統給線程提供運行的時間(這就是為什么這個狀態成為可運行而不是運行)

   事實上,運行中的線程被中斷,目的是為了讓他們線程獲得運行機會。線程調度的細節依賴於操作系統提供的服務。搶占式調度系統給每一個可運行線程一個時間片來執行任務。當時間片用完,操作系統剝奪該線程的運行權,並給另一個線程可運行機會。當選擇下一個線程時,操作系統考慮線程的優先級。

3. Blocked(被阻塞)

|

4. Waiting(等待)

|

5.Timed Waiting(計時等待)

   當線程處於被阻塞或等待狀態時,它暫時不活動。它不運行任何代碼且消耗最少的資源。直到線程調度器重新激活它。細節取決於它是怎樣達到非活動狀態的。

   * 當一個線程試圖獲取一個內部的對象鎖(而不是java.util.concurrent庫里的鎖), 而該鎖被其他線程持有,則該線程進入阻塞狀態。當其他線程釋放該鎖,並且線程調度器允許本線程持有它的時候,該線程將變成非阻塞狀態。

   * 當線程通知另一個線程通知調度器一個條件時,它自己進入等待狀態。在調用Object.wait方法或Thread.join方法,或者是等待java.util.concurrent庫中的Lock或Condition時,就會出現這種情況。實際上,被阻塞狀態與被等待狀態是有很大不同的。

   * 有幾個方法有一個超時參數。調用它們導致線程進入計時等待(timed waiting)狀態。這一狀態將一直保持到超時期滿或者接收到適當的通知。帶有超時參數的方法有Thread.sleep和Object.wait, Thrad.join, Lock.tryLock以及Condition.await的計時版。

6. Terminated(被終止)

   線程因如下兩個原因之一而被終止:

   1) 因為run方法正常退出而自然死亡

   2) 因為一個沒有捕獲的異常終止了run方法二意外死亡

 

二、線程初始化

線程初始化常用2種方式,繼承Thread類,實現Runnable接口

 1 public class Main implements Runnable {
 2 
 3     public static void main(String[] args) {
 4         // write your code here
 5         Thread t1 = new Thread(new Main());
 6         Thread t2 = new Thread() {
 7             @Override
 8             public void run() {
 9                 System.out.println("I'm Thread t2" + Thread.currentThread());
10             }
11         };
12         Thread t3 = new Thread(new Main());
13         System.out.println("主線程" + Thread.currentThread());
14         System.out.println("==============開始驗證start run的區別=================");
15         t1.start();
16         t1.run();
17         System.out.println("==============完成驗證start run的區別=================");
18 //
19 //        System.out.println("==============調起一個線程,繼承Thread類=================");
20 //        t2.start();
21 //        System.out.println("==============調起一個線程,繼承Thread類=================");
22 //
23 //        System.out.println("==============調起一個線程,實現Runnable接口,重寫run()方法=================");
24 //        t3.start();
25 //        System.out.println("==============調起一個線程,實現Runnable接口,重寫run()方法=================");
26     }
27 
28     @Override
29     public void run() {
30         System.out.println("I'm Thread Main" + Thread.currentThread());
31     }
32 }
2種實現方式

t1的構建方式就是是實現Runnable接口,通過Thread的構造方法,將一個實現了Runnable接口的實例對象傳入,就可以調用該線程的run方法了。

進入到Runnable接口的源碼中會發現,里面其實就一個run的抽象方法,但是配合Thread的構造方法,可以構造出Thread對象,相輔相成。

實際上里面還有很多構造方法,給注入的線程對象給定一個name,傳入一個線程組等等。

 

 

 Runnable接口  Thread類對應的構造方法
 

這是Thread類的run方法,執行目標對象的run方法

 

 tips:

1、關於Thread.run()和Thread.start()的問題

兩者都可以執行對象的run方法體中對象的代碼,但是,start()會創建一個新的線程並讓這個線程執行run()方法,而類.run()開啟的線程,貌似是新線程,實際上是串行執行run()中的代碼。

如圖,其實直接調用的run方法還是在main主線程中,而start的才是一個真正的新線程。

2、常用的話,還是實現Runnable接口,畢竟Java單繼承,只能認一個干爹,繼承Thread太浪費了。

 


免責聲明!

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



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