Java中的多線程 – JAVA編程語言程序開發技術文章

一、提要
java中的多線程算是java中的一個 很大的難點 ,雖然 看瞭 很多書 ,相信對於 很多 接觸 java不夠 深的人來說 ,多線程永遠都是心中的痛!
今天我們 就 通過大量的 例子 來 徹底把它征服 .

二、Runable接口
實現 Runable接口 是 實現多線程 的 一種方法。看例子
[java]
<SPAN style="FONT-SIZE: 14px">package thread; 
public class LiftOff implements Runnable { 
protected int countDown = 10; // Default  
private static int taskCount = 0; 
private final int id = taskCount++; 
public LiftOff() {} 
public LiftOff(int countDown) { 
this.countDown = countDown; 

public String status() { 
return "#" + id + "(" + 
(countDown > 0 ? countDown : "Liftoff!") + "), "; 

public void run() { 
while(countDown– > 0) { 
System.out.print(status()); 
Thread.yield(); 


} </SPAN> 

package thread;
public class LiftOff implements Runnable {
protected int countDown = 10; // Default
private static int taskCount = 0;
private final int id = taskCount++;
public LiftOff() {}
public LiftOff(int countDown) {
this.countDown = countDown;
}
public String status() {
return "#" + id + "(" +
(countDown > 0 ? countDown : "Liftoff!") + "), ";
}
public void run() {
while(countDown– > 0) {
System.out.print(status());
Thread.yield();
}
}
}

 

這個類的實現瞭 一個線程 :implements瞭Runable接口 ,實現瞭 run方法。
通常情況下,run方法中都會有意個循環 ,直到任務結束的時候才會跳出來。
線程的主要任務是循環十次,每次打印出 終端 狀態。Thread.yield()的作用是如果線程隊列中有線程等待,則阻塞自己,
將資源 交給 下一個等待的線程。

在main函數中 調用如下:
[java]
<SPAN style="FONT-SIZE: 14px">package thread; 
public class Main { 
public static void main(String[] arges) 

LiftOff tmp=new LiftOff(); 
tmp.run(); 
System.out.println("Waiting for LiftOff"); 


</SPAN> 

package thread;
public class Main {
public static void main(String[] arges)
{
LiftOff tmp=new LiftOff();
tmp.run();
System.out.println("Waiting for LiftOff");
}
}

將類型初始化實例時候 ,直接 調用run方法,線程就會開始運行。
運行結果:
#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(Liftoff!), Waiting for LiftOff
這裡實際上並沒有線程的概念,隻是用到瞭一些簡單的函數調用。
真正的線程需要將Runable裝到一個Thread中去。

三、Thread實例
傳統的使用Runable對象的方法就是把它作為一個參數傳給Thread的構造函數作為參數,然後調用Thread的start()方法來啟動線程。
start()方法對Runale進行 瞭 一些 必要的初始化,然後調用Runable的run方法。
將原main函數改寫如下:
[java]
<SPAN style="FONT-SIZE: 14px">package thread; 
public class Main { 
public static void main(String[] arges) 

Thread t = new Thread(new LiftOff()); 
t.start(); 
System.out.println("Waiting for LiftOff"); 
 
 

}</SPAN> 

package thread;
public class Main {
public static void main(String[] arges)
{
Thread t = new Thread(new LiftOff());
t.start();
System.out.println("Waiting for LiftOff");

}
}

運行結果:
Waiting for LiftOff
#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(Liftoff!),

運行的結果和之前的例子類似,但“Waiting for LiftOff“出現的位置不同瞭。
原理:在程序的第五行聲明瞭一個線程,並初始化,一個LiftOff對象作為參數傳進去。
第六行通過線程調用啟動瞭t線程,但main線程還可以繼續幹自己的事,cpu給的時間片還沒用完,於是“Waiting for LiftOff”就先被打印出來瞭,之後時間片被用完瞭,資源交給t線程,運行的內容被打印出來瞭。
下面是在main中啟動多個線程:
[java]
<SPAN style="FONT-SIZE: 14px">package thread; 
public class Main { 
 
 
public static void main(String[] arges) 

for(int i = 0; i < 5; i++) 
new Thread(new LiftOff()).start(); 
System.out.println("Waiting for LiftOff"); 

}</SPAN> 

package thread;
public class Main {

public static void main(String[] arges)
{
for(int i = 0; i < 5; i++)
new Thread(new LiftOff()).start();
System.out.println("Waiting for LiftOff");
}
}

修改一下LiftOff類:
[java]
<SPAN style="FONT-SIZE: 14px">package thread; 
public class LiftOff implements Runnable { 
private static int taskCount = 0; 
private final int id = taskCount++; 
public LiftOff() {} 
public void run() { 
System.out.println("startThreadId: " + id); 
try { 
Thread.sleep(1000); 
} catch (InterruptedException ignored) { 

System.out.println("endTHreadId: " + id); 

} </SPAN> 

package thread;
public class LiftOff implements Runnable {
private static int taskCount = 0;
private final int id = taskCount++;
public LiftOff() {}
public void run() {
System.out.println("startThreadId: " + id);
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
System.out.println("endTHreadId: " + id);
}
}

運行結果:
startThreadId: 0
startThreadId: 1
startThreadId: 2
startThreadId: 3
Waiting for LiftOff
startThreadId: 4
endTHreadId: 0
endTHreadId: 1
endTHreadId: 2
endTHreadId: 3
endTHreadId: 4

 

運行的結果反映瞭多線程運行的時候線程的調度。調度的規則主要由線程管理器來決定,在多核心的計算機中,線程管理器會把線程交由不同的cpu來完成。
由於線程的調度機制並不是確定的,不同版本的jdk所得到的結果是不同的。
對於打印結果的理解:運行之後哦main線程嘗試創建5個線程,但在創建#3進程的時候時間片用完瞭,main的Thread進入等待隊列,接著時間片論轉到最先進入線程隊列的#0進程->#0時間片用完(線程還沒銷毀)->時間片輪轉到#1->#1時間片用完….(看不懂的要回頭學一下操作系統中進程管理的知識)。

四、使用Executors
線程池由 Executor 框架提供。 Executor 框架將處理請求任務的提交和它的執行解耦。可以制定執行策略。在線程池中執行線程可以重用已經存在的線程,而不是創建新的線程,可以在處理多請求時抵消線程創建、消亡產生的開銷。
ExecutorService 的幾個重要方法:
1、shutdown方法:這個方法會平滑地關閉ExecutorService,當我們調用這個方法時,ExecutorService停止接受任何新的任務且等待已經提交的任務執行完成(已經提交的任務會分兩類:一類是已經在執行的,另一類是還沒有開始執行的),當所有已經提交的任務執行完畢後將會關閉ExecutorService。
2、awaitTermination方法:這個方法有兩個參數,一個是timeout即超時時間,另一個是unit即時間單位。這個方法會使線程等待timeout時長,當超過timeout時間後,會監測ExecutorService是否已經關閉,若關閉則返回true,否則返回false。一般情況下會和shutdown方法組合使用
直接看代碼:
[java]
<SPAN style="FONT-SIZE: 14px">package thread; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
public class Main { 
 
 
public static void main(String[] arges) 

//ExecutorService exec = Executors.newSingleThreadExecutor();  
ExecutorService exec = Executors.newFixedThreadPool(3); 
for(int i = 0; i <5; i++) 

try{ 
exec.execute(new LiftOff()); 
//Thread.sleep(1000);  
}catch(Exception e) 

e.printStackTrace(); 


exec.shutdown(); 
System.out.println("Waiting for LiftOff"); 

}</SPAN> 

package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {

public static void main(String[] arges)
{
//ExecutorService exec = Executors.newSingleThreadExecutor();
ExecutorService exec = Executors.newFixedThreadPool(3);
for(int i = 0; i <5; i++)
{
try{
exec.execute(new LiftOff());
//Thread.sleep(1000);
}catch(Exception e)
{
e.printStackTrace();
}
}
exec.shutdown();
System.out.println("Waiting for LiftOff");
}
}

運行結果:
startThreadId: 0
startThreadId: 1
Waiting for LiftOff
startThreadId: 2
endThreadId: 0      
startThreadId: 3
endThreadId: 1      
startThreadId: 4
endThreadId: 2      
endThreadId: 3      
endThreadId: 4      

 

newCachedThreadPool()創建一個可根據需要創建新線程的線程池,newSingleThreadExecutor()約等於newFixedThreadPool(1)

SingleThreadExecutor運行結果:
Waiting for LiftOff
startThreadId: 0
endThreadId: 0      
startThreadId: 1
endThreadId: 1      
startThreadId: 2
endThreadId: 2      
startThreadId: 3
endThreadId: 3      
startThreadId: 4
endThreadId: 4      
   

CachedThreadPool運行結果:
startThreadId: 0
startThreadId: 1
startThreadId: 2
startThreadId: 3
Waiting for LiftOff
startThreadId: 4
endThreadId: 0      
endThreadId: 1      
endThreadId: 2      
endThreadId: 3      
endThreadId: 4      
    

Executors還是有點難理解,需要很多的實戰才能有更好的體會,這裡點到為止。

 

五、Callable 和 Future接口  
 Callable是類似於Runnable的接口,實現Callable接口的類和實現Runnable的類都是可被其它線程執行的任務。  
 Callable和Runnable有幾點不同:  
 (1)Callable規定的方法是call(),而Runnable規定的方法是run().  
 (2)Callable的任務執行後可返回值,而Runnable的任務是不能返回值的。  
 (3)call()方法可拋出異常,而run()方法是不能拋出異常的。  
(4)運行Callable任務可拿到一個Future對象,  
 Future 表示異步計算的結果。它提供瞭檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。  
 通過Future對象可瞭解任務執行情況,可取消任務的執行,還可獲取任務執行的結果。  

代碼示例:

[java]
<SPAN style="FONT-SIZE: 14px">import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 
public class CallableTest { 
    /** *//**   
     * 自定義一個任務類,實現Callable接口   
     */     
    public static class MyCallableClass implements Callable{      
        // 標志位       
        private int flag = 0;      
        public MyCallableClass(int flag){      
            this.flag = flag;      
        }      
        public String call() throws Exception{      
            if (this.flag == 0){      
                // 如果flag的值為0,則立即返回       
                return "flag = 0";      
            }       
            if (this.flag == 1){      
                // 如果flag的值為1,做一個無限循環       
                try {      
                    while (true) {      
                        System.out.println("looping.");      
                        Thread.sleep(2000);      
                    }      
                } catch (InterruptedException e) {   
                //如果線程被終止,打印提示  
                    System.out.println("Interrupted");      
                }      
                return "false";      
            } else {      
                // falg不為0或者1,則拋出異常       
                throw new Exception("Bad flag value!");      
            }      
        }      
    }      
          
    public static void main(String[] args) {      
        // 定義3個Callable類型的任務       
        MyCallableClass task1 = new MyCallableClass(0);      
        MyCallableClass task2 = new MyCallableClass(1);      
        MyCallableClass task3 = new MyCallableClass(2);      
              
        // 創建一個執行任務的服務       
        ExecutorService es = Executors.newFixedThreadPool(3);      
        try {      
            // 提交並執行任務,任務啟動時返回瞭一個 Future對象,       
            // 如果想得到任務執行的結果或者是異常可對這個Future對象進行操作       
            Future future1 = es.submit(task1);      
            // 獲得第一個任務的結果,如果調用get方法,當前線程會等待任務執行完畢後才往下執行       
            System.out.println("task1: " + future1.get());      
                  
            Future future2 = es.submit(task2);      
            // 等待5秒後,再停止第二個任務。因為第二個任務進行的是無限循環       
            Thread.sleep(6000);      
            System.out.println("task2 cancel: " + future2.cancel(true));      
                  
            // 獲取第三個任務的輸出,因為執行第三個任務會引起異常       
            // 所以下面的語句將引起異常的拋出       
            Future future3 = es.submit(task3);      
            System.out.println("task3: " + future3.get());      
        } catch (Exception e){      
            System.out.println(e.toString());      
        }      
        // 停止任務執行服務       
        es.shutdownNow();      
    }      
 
 

</SPAN> 

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableTest {
    /** *//**  
     * 自定義一個任務類,實現Callable接口  
     */   
    public static class MyCallableClass implements Callable{    
        // 標志位    
        private int flag = 0;    
        public MyCallableClass(int flag){    
            this.flag = flag;    
        }    
        public String call() throws Exception{    
            if (this.flag == 0){    
                // 如果flag的值為0,則立即返回    
                return "flag = 0";    
            }     
            if (this.flag == 1){    
                // 如果flag的值為1,做一個無限循環    
                try {    
                    while (true) {    
                        System.out.println("looping.");    
                        Thread.sleep(2000);    
                    }    
                } catch (InterruptedException e) { 
                //如果線程被終止,打印提示
                    System.out.println("Interrupted");    
                }    
                return "false";    
            } else {    
                // falg不為0或者1,則拋出異常    
                throw new Exception("Bad flag value!");    
            }    
        }    
    }    
        
    public static void main(String[] args) {    
        // 定義3個Callable類型的任務    
        MyCallableClass task1 = new MyCallableClass(0);    
        MyCallableClass task2 = new MyCallableClass(1);    
        MyCallableClass task3 = new MyCallableClass(2);    
            
        // 創建一個執行任務的服務    
        ExecutorService es = Executors.newFixedThreadPool(3);    
        try {    
            // 提交並執行任務,任務啟動時返回瞭一個 Future對象,    
            // 如果想得到任務執行的結果或者是異常可對這個Future對象進行操作    
            Future future1 = es.submit(task1);    
            // 獲得第一個任務的結果,如果調用get方法,當前線程會等待任務執行完畢後才往下執行    
            System.out.println("task1: " + future1.get());    
                
            Future future2 = es.submit(task2);    
            // 等待5秒後,再停止第二個任務。因為第二個任務進行的是無限循環    
            Thread.sleep(6000);    
            System.out.println("task2 cancel: " + future2.cancel(true));    
                
            // 獲取第三個任務的輸出,因為執行第三個任務會引起異常    
            // 所以下面的語句將引起異常的拋出    
            Future future3 = es.submit(task3);    
            System.out.println("task3: " + future3.get());    
        } catch (Exception e){    
            System.out.println(e.toString());    
        }    
        // 停止任務執行服務    
        es.shutdownNow();    
    }    

}

 

運行結果:
task1: flag = 0
looping.
looping.
looping.
task2 cancel: true
Interrupted
java.util.concurrent.ExecutionException: java.lang.Exception: Bad flag value!

發佈留言