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
#234567891011121314##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
9101112#13#14#15#16 17181920212223242526272829 |
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()方法來停止正在執行的執行緒。
以下看一個範例:
1
2345678
9
10
11
12
#13
#14
#15
#16
17
18
19
| 20
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#34#567##8
9
10111213#14#15#16# 17181920#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()判斷中斷標誌是否被置位來中斷線程的執行。例如下面這段程式碼:
1 ##2
1##2
#3
4
5
6
##10
11
12
13
14
15
16
| # #17
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;
| while(!is1 System.out.println(i+" while迴圈"); i++; }#1
# 運作會發現,列印若干個值之後,while循環就停止列印了。 但是一般情況下不建議透過這種方式中斷線程,一般會在MyThread類別中增加一個屬性 isStop來標誌是否結束while循環,然後再在while循環中判斷isStop的值。
#1234567#89 ##10 11
12
13
| 14
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中文網其他相關文章!