首頁 > Java > java教程 > Java並發程式設計:Thread類別怎麼使用?

Java並發程式設計:Thread類別怎麼使用?

零下一度
發布: 2017-06-23 09:16:04
原創
2026 人瀏覽過
Java並發程式設計:Thread類別的使用
  
  以下是本文的目錄大綱:
  一.執行緒的狀態
  二.上下文切換
  三.Thread類別中的方法
  
  轉載自:
   

一.執行緒的狀態

   一.執行緒的狀態
   
一個。 ##  
執行緒從創建到最終的消亡,要經歷若干個狀態。一般來說,執行緒包含以下這些狀態:創建(new)、就緒(runnable)、運行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。

  

  下面這副圖描述了線程從創建到消亡之間的狀態:

 
  在有些教程上將blocked、waiting、time waiting統稱為阻塞狀態,這個也是可以的,只不過這裡我想將線程的狀態和Java中的方法調用聯繫起來,所以將waiting和time waiting兩個狀態分離出來。
二.上下文切換

  

對於單核心CPU來說(對於多核心CPU,此處就理解為一個核心),CPU在一個時刻只能運行一個線程,當在運行一個線程的過程中轉去運行另外一個線程,這個叫做線程上下文切換(對於進程也是類似)。
  

三.Thread類別中的方法

  透過檢視java.lang.Thread類別的原始碼可知:
  
  Thread類別實作了Runnable接口,在Thread類別中,有一些比較關鍵的屬性,例如name是表示Thread的名字,可以透過Thread類別的建構器中的參數來指定線程名字,priority表示線程的優先權(最大值為10, 最小值為1,預設值為5),daemon表示線程是否為守護線程,target表示要執行的任務。
  
下面是Thread類別中常用的方法:
  以下是幾個關係到執行緒運作狀態的方法:
  1)start方法
  start()用來啟動一個線程,當呼叫start方法後,系統才會開啟一個新的線程來執行使用者定義的子任務,在這個過程中,會為對應的線程分配需要的資源。
  2)run方法
  run()方法是不需要使用者來呼叫的,當透過start方法啟動一個執行緒之後,當執行緒獲得了CPU執行時間,便進入run方法體去執行具體的任務。注意,繼承Thread類別必須重寫run方法,在run方法中定義具體要執行的任務。   3)sleep方法  sleep方法有兩個重載版本:
#################### ########1######2######3#############sleep(long millis)     //參數為毫秒##### # ######sleep(long millis,int nanoseconds)    //第一參數為毫秒,第二個參數為奈秒################
  sleep相當於讓執行緒睡眠,交出CPU,讓CPU去執行其他的任務。
  但是有一點要非常注意,sleep方法不會釋放鎖,也就是說如果當前執行緒持有對某個物件的鎖,則即使呼叫sleep方法,其他執行緒也無法存取這個物件。看下面這個範例就很清楚了:
#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
26
27
28
29
#30
31
32
33
public class Test {
     
#    private int i = 10;
    private Object object = new Object();
     
    public static void main( Test();
        MyThread thread1 = test.new MyThread();
        MyThread thread
#        thread2.start();
    } 
     
     @Override
        public void run() {
            synchronized (ob #             System.out.println("i:"+i);
                try {
               
##                    Thread.currentThread( ).sleep(10000);
                } catch (InterruptedException e) {
   
                }
               (問題"+Thread.currentThread().getName()+"睡眠結束");
                i++;
## 
##            }
   輸出結果:
  

 

  從上面輸出結果可以看出,當Thread-0進入睡眠狀態之後,Thread-1並沒有去執行具體的任務。只有當Thread-0執行完之後,此時Thread-0釋放了物件鎖,Thread-1才開始執行。
  注意,如果呼叫了sleep方法,必須捕獲InterruptedException異常或將該異常向上層拋出。當執行緒睡眠時間滿後,不一定會立即執行,因為此時CPU可能正在執行其他的任務。所以說呼叫sleep方法相當於讓執行緒進入阻塞狀態。
  4)yield方法
  呼叫yield方法會讓目前執行緒交出CPU權限,讓CPU去執行其他的執行緒。它跟sleep方法類似,同樣不會釋放鎖定。但是yield不能控制特定的交出CPU的時間,另外,yield方法只能讓擁有相同優先權的執行緒有取得CPU執行時間的機會。
  注意,呼叫yield方法並不會讓執行緒進入阻塞狀態,而是讓執行緒重回就緒狀態,它只需要等待重新取得CPU執行時間,這一點是和sleep方法不一樣的。
  5)join方法
  join方法有三個重載版本:
###### #######1######2######3#############join()######join(long millis)     //參數為毫秒######join(long millis,int nanoseconds)    //第一參數為毫秒,第二個參數為奈秒###############
   假如在main執行緒中,呼叫thread.join方法,則main方法會等待thread執行緒​​執行完畢或等待一定的時間。如果呼叫的是無參join方法,則等待thread執行完畢,如果呼叫的是指定了時間參數的join方法,則等待一定的事件。
  看下面一個範例:
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
26
27
28
29
30
public class Test {
##     
    public static void main(String[] args) throws IOException ("進入執行緒"+Thread.currentThread().getName());
        Test test = new Test();
        MyThread thread1 = test. MyThreadthread1 = test.Preadthread1
        thread1.start();
#        try {
          p.
#            thread1.join();
            @);
             } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            } 
#     
    class MyThread extends Thread{
        @Override
   ystem.out.println("進入線程"+Thread .currentThread().getName());
            try {
               } catch (InterruptedException e) {
                // TODO: handle Thread.currentThread().getName()+"執行完畢");
        }
    }
}
###########################################################################################################
   輸出結果:
  

 

  可以看出,當呼叫thread1.join()方法後,main執行緒會進入等待,然後等待thread1執行完後再繼續執行。
  其實呼叫join方法是呼叫了Object的wait方法,這個可以透過檢視原始碼得知:
  

 

#  wait方法會讓執行緒進入阻塞狀態,並且會釋放執行緒佔有的鎖,並交出CPU執行權限。
  由於wait方法會讓執行緒釋放物件鎖定,所以join方法同樣會讓執行緒釋放對一個物件所持有的鎖定。具體的wait方法使用在後面文章中給出。
  6)interrupt方法
  interrupt,顧名思義,即中斷的意思。單獨呼叫interrupt方法可以使得處於阻塞狀態的執行緒拋出一個異常,也就說,它可以用來中斷一個正處於阻塞狀態的執行緒;另外,透過interrupt方法和isInterrupted()方法來停止正在執行的執行緒。
  以下看一個範例:
20}
1
2
3
4
5
6
7
8
9
10
11
12
#13
#14
#15
#16
17
18
19
21
22
23
24
25
26
27
28
##public class Test {
     
    public static void main(String[] args) throws IOException  {
        Test test = new Test();
        MyThread thread = test. new MyThread();
        thread.start();
        try {
         } catch (InterruptedException e) {
             
        }
##1      
    class MyThread extends Thread{
        @Override
        public voidrun() {
#       System.out.println("進入睡眠狀態") ;
                Thread.currentThread().sleep(10000);
              } catch (InterruptedException e) {
                System.out.println("取得中斷異常");
      ("run方法執行完畢");
#        }
#    }
#######################
   輸出結果:
  

  從這裡可以看出,透過interrupt方法可以中斷處於阻塞狀態的執行緒。那麼能不能中斷處於非阻塞狀態的執行緒呢?看下面這個範例:
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 Test {
     
    public static void main(String[] args) throws IO
##        Test test = new Test();
        MyThread thread = test.new MyThread();# 
            Thread.currentThread().sleep(2000);
##        } c       }
        thread.interrupt ();
    } 
#     
    class MyThread extends Thread{
   
            int i = = 0;
            while(i< .println(i+" while迴圈");
                i++;
            }
        }
    }
}
   執行程式會發現,while迴圈會一直運作直到變數i的值超出Integer.MAX_VALUE。所以說直接呼叫interrupt方法不能中斷正在執行中的執行緒。
  但是如果配合isInterrupted()能夠中斷正在運行的線程,因為呼叫interrupt方法相當於將中斷標誌位置為true,那麼可以透過呼叫isInterrupted()判斷中斷標誌是否被置位來中斷線程的執行。例如下面這段程式碼:
# #17            while(!is1   System.out.println(i+" while迴圈");                i++;
1
##2
1
##2
#3
4
5
6
##10
11
12
13
14
15
16
18
19
20
#21
#22
#23
# 24
25
public class Test {
     
public class Test {
     
    public static void main(String[] args) throws IOException public static void main(String[] args) throws IOException public static void main(String[] args) throws IOException public static void main(String[] args) throws IOException public static void main(String[] args) throws IOException public static main(String[]
##        Test test = new Test();
        MyThread thread = test.new {
            Thread.currentThread().sleep(2000);
##        } c        }
        thread. interrupt();
    } 
     
    class MyThread extends Thread{
        @Override
        public void run() {
            int i = 0;
            }#1 
#   運作會發現,列印若干個值之後,while循環就停止列印了。
  但是一般情況下不建議透過這種方式中斷線程,一般會在MyThread類別中增加一個屬性 isStop來標誌是否結束while循環,然後再在while循環中判斷isStop的值。 14
#1
2
3
4
5
6
7
#8
9
##10
11
12
13
class MyThread extends Thread{
        private volatile
#        private volatile boolean isStop = false;
        @Override
        public void run() {
#      while(!isStop){
                i++;
            )#         public void setStop(boolean stop){
            this.isStop = stop;
        }
#    }
#
   那麼就可以在外面透過呼叫setStop方法來終止while循環。
  7)stop方法
  stop方法已經是一個廢棄的方法,它是一個不安全的方法。因為調用stop方法會直接終止run方法的調用,並且會拋出一個ThreadDeath錯誤,如果線程持有某個物件鎖的話,會完全釋放鎖,導致物件狀態不一致。所以stop方法基本上是不會被用到的。
  8)destroy方法
  destroy方法也是廢棄的方法。基本不會被使用。
  以下是幾個關係到執行緒屬性:
  1)getId
  用來得到執行緒ID
  2)getName和setName
  2)getName和setName
#  用來得到或設定執行緒名稱。
  3)getPriority和setPriority
  用來取得和設定執行緒優先權。
  4)setDaemon和isDaemon
  用來設定線程是否成為守護線程和判斷線程是否是守護線程。
  守護線程和使用者線程的區別在於:守護線程依賴創建它的線程,而使用者線程則不依賴。舉個 簡單的例子:如果在main線程中建立了一個守護線程,當main方法運行完畢之後,守護線程也會隨著消亡。而使用者執行緒則不會,使用者執行緒會一直運行直到其 運行完畢。在JVM中,像垃圾收集器線程就是守護線程。
  Thread類別有一個比較常用的靜態方法currentThread()用來取得目前執行緒。

  在上面已經說到了Thread類別中的大部分方法,那麼Thread類別中的方法呼叫到底會引起執行緒狀態發生怎樣的變化呢?下面一張圖就是在上面的圖表上改良而來的:
 
####### ###### #####

以上是Java並發程式設計:Thread類別怎麼使用?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板