人生不設限,高飛吧~
在Java5之後,併發執行緒這塊發生了根本的變化,最重要的莫過於新的啟動、排程、管理執行緒的一大堆API了。在Java5以後,通過Executor來啟動執行緒比用Thread的start()更好。在新特徵中,可以很容易控制執行緒的啟動、執行和關閉過程,還可以很容易使用執行緒池的特性。
一、建立任務
任務就是一個實現了Runnable介面的類。
建立的時候實run方法即可。
二、執行任務
通過java.util.concurrent.ExecutorService介面物件來執行任務,該介面物件通過工具類java.util.concurrent.Executors的靜態方法來建立。
Executors此包中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。
ExecutorService提供了管理終止的方法,以及可為跟蹤一個或多個非同步任務執行狀況而生成 Future 的方法。 可以關閉 ExecutorService,這將導致其停止接受新任務。關閉後,執行程式將最後終止,這時沒有任務在執行,也沒有任務在等待執行,並且無法提交新任務。
executorService.execute(new TestRunnable());
1、建立ExecutorService
通過工具類java.util.concurrent.Executors的靜態方法來建立。
Executors此包中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。
比如,建立一個ExecutorService的例項,ExecutorService實際上是一個執行緒池的管理工具:
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newFixedThreadPool(3);
ExecutorService executorService = Executors.newSingleThreadExecutor();
2、將任務新增到執行緒去執行
當將一個任務新增到執行緒池中的時候,執行緒池會為每個任務建立一個執行緒,該執行緒會在之後的某個時刻自動執行。
三、關閉執行服務物件
executorService.shutdown();
四、綜合例項
package concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by IntelliJ IDEA.
*
* @author leizhimin 2008-11-25 14:28:59
*/
public class TestCachedThreadPool {
public static void main(String[] args) {
// ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newFixedThreadPool(5); // ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
executorService.execute(new TestRunnable());
System.out.println("************* a" + i + " *************");
}
executorService.shutdown();
}
}
class TestRunnable implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName() + "執行緒被呼叫了。");
while (true) {
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
執行結果:
************* a0 *************
************* a1 *************
pool-1-thread-2執行緒被呼叫了。
************* a2 *************
pool-1-thread-3執行緒被呼叫了。
pool-1-thread-1執行緒被呼叫了。
************* a3 *************
************* a4 *************
pool-1-thread-4執行緒被呼叫了。
pool-1-thread-5執行緒被呼叫了。
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-5
pool-1-thread-4
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-5
pool-1-thread-4
五、獲取任務的執行的返回值
在Java5之後,任務分兩類:一類是實現了Runnable介面的類,一類是實現了Callable介面的類。兩者都可以被ExecutorService執行,但是Runnable任務沒有返回值,而Callable任務有返回值。並且Callable的call()方法只能通過ExecutorService的submit(Callable<T> task) 方法來執行,並且返回一個 <T> Future<T>,是表示任務等待完成的 Future.
public interface Callable<V>返回結果並且可能丟擲異常的任務。實現者定義了一個不帶任何引數的叫做 call 的方法。
Callable 介面類似於 Runnable,兩者都是為那些其例項可能被另一個執行緒執行的類設計的。但是 Runnable 不會返回結果,並且無法丟擲經過檢查的異常。
Executors 類包含一些從其他普通形式轉換成 Callable 類的實用方法。
Callable中的call()方法類似Runnable的run()方法,就是前者有返回值,後者沒有。
當將一個Callable的物件傳遞給ExecutorService的submit方法,則該call方法自動在一個執行緒上執行,並且會返回執行結果Future物件。
同樣,將Runnable的物件傳遞給ExecutorService的submit方法,則該run方法自動在一個執行緒上執行,並且會返回執行結果Future物件,但是在該Future物件上呼叫get方法,將返回null.
遺憾的是,在Java API文件中,這塊介紹的很糊塗,估計是翻譯人員還沒搞清楚的緣故吧。或者說是註釋不到位。下面看個例子:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* Callable介面測試
*
* @author leizhimin 2008-11-26 9:20:13
*/
public class CallableDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> resultList = new ArrayList<Future<String>>();
//建立10個任務並執行
for (int i = 0; i < 10; i++) {
//使用ExecutorService執行Callable型別的任務,並將結果儲存在future變數中
Future<String> future = executorService.submit(new TaskWithResult(i));
//將任務執行結果儲存到List中
resultList.add(future);
}
//遍歷任務的結果
for (Future<String> fs : resultList) {
try {
System.out.println(fs.get()); //列印各個執行緒(任務)執行的結果
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
//啟動一次順序關閉,執行以前提交的任務,但不接受新任務。如果已經關閉,則呼叫沒有其他作用。
executorService.shutdown();
}
}
}
}
class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
/**
* 任務的具體過程,一旦任務傳給ExecutorService的submit方法,則該方法自動在一個執行緒上執行。
*
* @return
* @throws Exception
*/
public String call() throws Exception {
System.out.println("call()方法被自動呼叫,幹活!!! " + Thread.currentThread().getName());
//一個模擬耗時的操作
for (int i = 999999; i > 0; i--) ;
return "call()方法被自動呼叫,任務的結果是:" + id + " " + Thread.currentThread().getName();
}
}
執行結果:
call()方法被自動呼叫,幹活!!! pool-1-thread-1
call()方法被自動呼叫,幹活!!! pool-1-thread-3
call()方法被自動呼叫,幹活!!! pool-1-thread-4
call()方法被自動呼叫,幹活!!! pool-1-thread-6
call()方法被自動呼叫,幹活!!! pool-1-thread-2
call()方法被自動呼叫,幹活!!! pool-1-thread-5
call()方法被自動呼叫,任務的結果是:0 pool-1-thread-1
call()方法被自動呼叫,任務的結果是:1 pool-1-thread-2
call()方法被自動呼叫,幹活!!! pool-1-thread-2
call()方法被自動呼叫,幹活!!! pool-1-thread-6
call()方法被自動呼叫,幹活!!! pool-1-thread-4
call()方法被自動呼叫,任務的結果是:2 pool-1-thread-3
call()方法被自動呼叫,幹活!!! pool-1-thread-3
call()方法被自動呼叫,任務的結果是:3 pool-1-thread-4
call()方法被自動呼叫,任務的結果是:4 pool-1-thread-5
call()方法被自動呼叫,任務的結果是:5 pool-1-thread-6
call()方法被自動呼叫,任務的結果是:6 pool-1-thread-2
call()方法被自動呼叫,任務的結果是:7 pool-1-thread-6
call()方法被自動呼叫,任務的結果是:8 pool-1-thread-4
call()方法被自動呼叫,任務的結果是:9 pool-1-thread-3
Process finished with exit code 0
一個 ExecutorService,它使用可能的幾個池執行緒之一執行每個提交的任務,通常使用 Executors 工廠方法配置。
執行緒池可以解決兩個不同問題:由於減少了每個任務呼叫的開銷,它們通常可以在執行大量非同步任務時提供增強的效能,並且還可以提供繫結和管理資源(包括執行集合任務時使用的執行緒)的方法。每個 ThreadPoolExecutor 還維護著一些基本的統計資料,如完成的任務數。
為了便於跨大量上下文使用,此類提供了很多可調整的引數和擴充套件掛鉤。但是,強烈建議程式設計師使用較為方便的 Executors 工廠方法Executors.newCachedThreadPool()(無界執行緒池,可以進行自動執行緒回收)、Executors.newFixedThreadPool(int)(固定大小執行緒池)和Executors.newSingleThreadExecutor()(單個後臺執行緒),它們均為大多數使用場景預定義了設定。