實現多執行緒的兩種方法:繼承Thread類或實現Runnable介面

Java中實現多執行緒有兩種方法:繼承Thread類和實現Runnable介面,在程式開發中只要是多執行緒,我們一般都是實現Runnable介面,原因歸結為一點:實現介面比繼承類要好。

多執行緒的第一種實現方式:繼承Thread類

步驟如下

建立一個繼承Thread的類(假定為A),並重寫Thread的run方法 構造一個A類物件,假定為aa 呼叫aa的start方法。(start方法是從Thread繼承過來的)

具體例子如下

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
34
35
<code class="hljs" axapta="">package org.wrh.concurrent;
/*
* 火車站多站點賣票程式
* */
class MyThread extends Thread{ 
private int ticketNum = 10
public void run(){ 
for (int i=0;i<10;i++) 
if(ticketNum > 0){ 
System.out.println(Thread.currentThread().getName()+    正在賣第  + ticketNum+張票); 
ticketNum--;
public class ThreadDemo01{ 
public static void main(String[] args){ 
/*
* 共三個執行緒物件,分別為各個執行緒制定了執行緒的名字
* */
Thread t1= new MyThread();
t1.setName(執行緒A);
t1.start();
Thread t2= new MyThread();
t2.setName(執行緒B);
t2.start();
Thread t3= new MyThread();
t3.setName(執行緒C);
t3.start();
}
</code>

程式執行的結果如下:

執行緒A 正在賣第 10張票
執行緒B 正在賣第 10張票
執行緒A 正在賣第 9張票
執行緒B 正在賣第 9張票
執行緒A 正在賣第 8張票
執行緒B 正在賣第 8張票
執行緒C 正在賣第 10張票
執行緒C 正在賣第 9張票
執行緒C 正在賣第 8張票
執行緒C 正在賣第 7張票
執行緒C 正在賣第 6張票
執行緒C 正在賣第 5張票
執行緒C 正在賣第 4張票
執行緒C 正在賣第 3張票
執行緒C 正在賣第 2張票
執行緒C 正在賣第 1張票
執行緒A 正在賣第 7張票
執行緒A 正在賣第 6張票
執行緒A 正在賣第 5張票
執行緒A 正在賣第 4張票
執行緒A 正在賣第 3張票
執行緒A 正在賣第 2張票
執行緒A 正在賣第 1張票
執行緒B 正在賣第 7張票
執行緒B 正在賣第 6張票
執行緒B 正在賣第 5張票
執行緒B 正在賣第 4張票
執行緒B 正在賣第 3張票
執行緒B 正在賣第 2張票
執行緒B 正在賣第 1張票

從結果中可以看出,A、B、C三個執行緒每個執行緒相互獨立的賣了10張票,但實際的火車站售票中,是需要多個執行緒去共同去賣N張票。

注意

Thread中start方法的功能是建立一個新的執行緒,並自動呼叫該執行緒的run方法,直接呼叫run方法是不會建立一個新的執行緒的。 執行一個執行緒實際上就是執行該執行緒的run方法裡面的程式碼。 執行完aa.start()方法後並不表示aa所對應的執行緒就一定會得到執行,只是表示此執行緒具有了被CPU立即執行的資格。但由於想搶佔CPU執行的執行緒很多,CPU並不一定會立即執行aa所對應的執行緒。執行緒的執行由操作系統控制:優先順序、時間長短、最長等待時間等。 一個Thread物件不能呼叫兩次start(),否則會丟擲異常。

多執行緒的第二種實現方式:實現Runnable介面

步驟如下:

定義一個實現了Runnable介面的類,假定為A 建立A類的物件aa 利用aa構造一個Thread物件tt,Thread tt=new Thread(aa) 呼叫tt的start方法:`tt.start()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<code class="hljs" axapta="">package org.wrh.concurrent;
class MyThread_1 implements Runnable{ 
private int ticketNum = 20
public void run(){ 
for (int i=0;i<10;i++) 
if(ticketNum > 0){ 
System.out.println(Thread.currentThread().getName()+    正在賣第  + ticketNum+張票); 
ticketNum--;
public class ThreadDemo02{ 
public static void main(String[] args){ 
MyThread_1 my = new MyThread_1(); 
new Thread(my).start(); 
new Thread(my).start(); 
new Thread(my).start(); 
}
</code>

程式執行結果如下:

Thread-1 正在賣第 20張票
Thread-2 正在賣第 20張票
Thread-0 正在賣第 20張票
Thread-2 正在賣第 18張票
Thread-1 正在賣第 19張票
Thread-2 正在賣第 16張票
Thread-0 正在賣第 17張票
Thread-2 正在賣第 14張票
Thread-1 正在賣第 15張票
Thread-2 正在賣第 12張票
Thread-0 正在賣第 13張票
Thread-2 正在賣第 10張票
Thread-1 正在賣第 11張票
Thread-2 正在賣第 8張票
Thread-0 正在賣第 9張票
Thread-2 正在賣第 6張票
Thread-1 正在賣第 7張票
Thread-2 正在賣第 4張票
Thread-0 正在賣第 5張票
Thread-2 正在賣第 2張票
Thread-1 正在賣第 3張票
Thread-0 正在賣第 1張票

從上面結果可以看出:

第一點:這三個執行緒並不是相互獨立的賣20張票,而是一起賣20張票。 第二點:這三個執行緒賣票的過程中有衝突,例如,都賣第20張票,

產生第二點的原因為:

一個執行緒在判斷ticketNum為20>0後,還沒有來得及減1,另一個執行緒此時也判斷ticketNum為20>0,還沒有來得及減1,那麼接下來第三個執行緒開始執行發現ticketNum還是20>0,於是就這樣三個執行緒把第20張票賣了3次。這就需要加入同步操作(即互斥鎖),確保同一時刻只有一個執行緒在執行每次for迴圈中的操作。而在第一種方法中,並不需要加入同步操作,因為每個執行緒執行自己Thread物件中的程式碼,不存在多個執行緒共同執行同一個方法的情況。

加同步操作後的程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<code class="hljs" axapta="">package org.wrh.concurrent;
class MyThread_1 implements Runnable{ 
private int ticketNum = 20
public synchronized void run(){ 
for (int i=0;i<10;i++) 
if(ticketNum > 0){ 
System.out.println(Thread.currentThread().getName()+    正在賣第  + ticketNum+張票); 
ticketNum--;
public class ThreadDemo02{ 
public static void main(String[] args){ 
MyThread_1 my = new MyThread_1(); 
new Thread(my).start(); 
new Thread(my).start(); 
new Thread(my).start(); 
}
</code>

上面的程式碼只是在原有的程式碼的基礎上在run方法前加上了synchronized關鍵字來限定即可完成了鎖的功能。
程式的結果如下:

Thread-0 正在賣第 20張票
Thread-0 正在賣第 19張票
Thread-0 正在賣第 18張票
Thread-0 正在賣第 17張票
Thread-0 正在賣第 16張票
Thread-0 正在賣第 15張票
Thread-0 正在賣第 14張票
Thread-0 正在賣第 13張票
Thread-0 正在賣第 12張票
Thread-0 正在賣第 11張票
Thread-2 正在賣第 10張票
Thread-2 正在賣第 9張票
Thread-2 正在賣第 8張票
Thread-2 正在賣第 7張票
Thread-2 正在賣第 6張票
Thread-2 正在賣第 5張票
Thread-2 正在賣第 4張票
Thread-2 正在賣第 3張票
Thread-2 正在賣第 2張票
Thread-2 正在賣第 1張票

總共買了20張票,且每張票只賣了一次,這就是實現了同步鎖的功能

最後對執行緒控制的基本方法進行一個介紹
(1)isAlive():判斷執行緒是否還“活”著,即執行緒是否還未終止
(2)getPriority():獲得執行緒的優先順序數值,0表示執行緒優先順序最高。
(3)setPriority():設定執行緒的優先順序
(4)join():用執行緒物件呼叫,如果在一個執行緒A中呼叫另一個執行緒B的join方法,執行緒A將會等待執行緒B執行完畢後再執行。
(5)yield():讓出CPU,當前執行緒進入就緒佇列等待排程。直接用Thread類呼叫,yield讓出CPU執行權給同等級的執行緒,如果沒有相同級別的執行緒在等待CPU的執行權,則該執行緒繼續執行