*1:
定義了幾個任務
*2:
初始了任務執行工具。任務的執行框架將會在后面解釋。
*3:
執行任務,任務啟動時返回了一個
Future
對象,如果想得到任務執行的結果或者是異常可對這個
Future
對象進行操作。
Future
所含的值必須跟
Callable
所含的值對映,比如說例子中
Future
對印
Callable
*4:
任務
1
正常執行完畢,
future1.get()
會返回線程的值
*5:
任務
2
在進行一個死循環,調用
future2.cancel(true)
來中止此線程。傳入的參數標明是否可打斷線程,
true
表明可以打斷。
*6:
任務
3
拋出異常,調用
future3.get()
時會引起異常的拋出。
?
運行
Executor
會有以下運行結果:
looping....
Task done. //*1
looping....
looping....//*2
looping....
looping....
looping....
looping....
Thread 2 terminated? :true //*3
//*4
java.util.concurrent.ExecutionException: java.lang.Exception: Callable terminated with Exception!
??????? at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:205)
??????? at java.util.concurrent.FutureTask.get(FutureTask.java:80)
??????? at concurrent.Executor.main(Executor.java:43)
??????? …….
|
*1:
任務
1
正常結束
*2:
任務
2
是個死循環,這是它的打印結果
*3:
指示任務
2
被取消
*4:
在執行
future3.get()
時得到任務
3
拋出的異常
3:新的任務執行架構
?? 在
Java 5.0
之前啟動一個任務是通過調用
Thread
類的
start()
方法來實現的,任務的提于交和執行是同時進行的,如果你想對任務的執行進行調度或是控制同時執行的線程數量就需要額外編寫代碼來完成。
5.0
里提供了一個新的任務執行架構使你可以輕松地調度和控制任務的執行,并且可以建立一個類似數據庫連接池的線程池來執行任務。這個架構主要有三個接口和其相應的具體類組成。這三個接口是
Executor, ExecutorService
和
ScheduledExecutorService
,讓我們先用一個圖來顯示它們的關系:
?
? 圖的左側是接口,圖的右側是這些接口的具體類。注意
Executor
是沒有直接具體實現的。
Executor
接口:
是用來執行
Runnable
任務的,它只定義一個方法:
-
execute(Runnable command)
:執行
Ruannable
類型的任務
ExecutorService
接口:
ExecutorService
繼承了
Executor
的方法,并提供了執行
Callable
任務和中止任務執行的服務,其定義的方法主要有:
-
submit(task)
:可用來提交
Callable
或
Runnable
任務,并返回代表此任務的
Future
對象
-
invokeAll(collection of tasks)
:批處理任務集合,并返回一個代表這些任務的
Future
對象集合
-
shutdown()
:在完成已提交的任務后關閉服務,不再接受新任務
-
shutdownNow()
:停止所有正在執行的任務并關閉服務。
-
isTerminated()
:測試是否所有任務都執行完畢了。
-
isShutdown()
:測試是否該
ExecutorService
已被關閉
ScheduledExecutorService
接口
在
ExecutorService
的基礎上,
ScheduledExecutorService
提供了按時間安排執行任務的功能,它提供的方法主要有:
-
schedule(task, initDelay):
安排所提交的
Callable
或
Runnable
任務在
initDelay
指定的時間后執行。
-
scheduleAtFixedRate()
:安排所提交的
Runnable
任務按指定的間隔重復執行
-
scheduleWithFixedDelay()
:安排所提交的
Runnable
任務在每次執行完后,等待
delay
所指定的時間后重復執行。
代碼:
ScheduleExecutorService
的例子
public class ScheduledExecutorServiceTest {
??????? public static void main(String[] args)
?????????????? throws InterruptedException, ExecutionException{
?????????????? //*1
??????????????? ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
??????????????? //*2
??????????????? Runnable task1 = new Runnable() {
???????????????????? public void run() {
??????????????????????? System.out.println("Task repeating.");
???????????????????? }
??????????????? };
??????????????? //*3
??????????????? final ScheduledFuture future1 =
??????????????????????? service.scheduleAtFixedRate(task1, 0, 1, TimeUnit.SECONDS);
??????????????? //*4
??????????????? ScheduledFuture future2 = service.schedule(new Callable(){
???????????????????? public String call(){
???????????????????????????? future1.cancel(true);
???????????????????????????? return "task cancelled!";
???????????????
?????}
??????????????? }, 5, TimeUnit.SECONDS);
??????????????? System.out.println(future2.get());
//*5
service.shutdown();
??????? }
}
|
?? 這個例子有兩個任務,第一個任務每隔一秒打印一句“
Task repeating
”
,
第二個任務在
5
秒鐘后取消第一個任務。
*1:
初始化一個
ScheduledExecutorService
對象,這個對象的線程池大小為
2
。
*2:
用內函數的方式定義了一個
Runnable
任務。
*3:
調用所定義的
ScheduledExecutorService
對象來執行任務,任務每秒執行一次。能重復執行的任務一定是
Runnable
類型。注意我們可以用
TimeUnit
來制定時間單位,這也是
Java 5.0
里新的特征,
5.0
以前的記時單位是微秒,現在可精確到奈秒。
*4:
調用
ScheduledExecutorService
對象來執行第二個任務,第二個任務所作的就是在
5
秒鐘后取消第一個任務。
*5:
關閉服務。
Executors
類
?? 雖然以上提到的接口有其實現的具體類,但為了方便
Java 5.0
建議使用
Executors
的工具類來得到
Executor
接口的具體對象,需要注意的是
Executors
是一個類,不是
Executor
的復數形式。
Executors
提供了以下一些
static
的方法:
-
callable(Runnable task):
將
Runnable
的任務轉化成
Callable
的任務
-
newSingleThreadExecutor:
產生一個
ExecutorService
對象,這個對象只有一個線程可用來執行任務,若任務多于一個,任務將按先后順序執行。
-
newCachedThreadPool():
產生一個
ExecutorService
對象,這個對象帶有一個線程池,線程池的大小會根據需要調整,線程執行完任務后返回線程池,供執行下一次任務使用。
-
newFixedThreadPool(int poolSize)
:產生一個
ExecutorService
對象,這個對象帶有一個大小為
poolSize
的線程池,若任務數量大于
poolSize
,任務會被放在一個
queue
里順序執行。
-
newSingleThreadScheduledExecutor
:產生一個
ScheduledExecutorService
對象,這個對象的線程池大小為
1
,若任務多于一個,任務將按先后順序執行。
-
newScheduledThreadPool(int poolSize):
產生一個
ScheduledExecutorService
對象,這個對象的線程池大小為
poolSize
,若任務數量大于
poolSize
,任務會在一個
queue
里等待執行
以下是得到和使用
ExecutorService
的例子:
代碼:如何調用
Executors
來獲得各種服務對象
//Single Threaded ExecutorService
???? ExecutorService singleThreadeService = Executors.newSingleThreadExecutor();
//Cached ExecutorService
???? ExecutorService cachedService = Executors.newCachedThreadPool();
//Fixed number of ExecutorService
???? ExecutorService fixedService = Executors.newFixedThreadPool(3);
//Single ScheduledExecutorService
???? ScheduledExecutorService singleScheduledService =
????????? Executors.newSingleThreadScheduledExecutor();
//Fixed number of ScheduledExecutorService
ScheduledExecutorService fixedScheduledService =
???? Executors.newScheduledThreadPool(3);
|
4:Lockers和Condition接口
?? 在多線程編程里面一個重要的概念是鎖定,如果一個資源是多個線程共享的,為了保證數據的完整性,在進行事務性操作時需要將共享資源鎖定,這樣可以保證在做事務性操作時只有一個線程能對資源進行操作,從而保證數據的完整性。在
5.0
以前,鎖定的功能是由
Synchronized
關鍵字來實現的,這樣做存在幾個問題:
-
每次只能對一個對象進行鎖定。若需要鎖定多個對象,編程就比較麻煩,一不小心就會出現死鎖現象。
-
如果線程因拿不到鎖定而進入等待狀況,是沒有辦法將其打斷的
在
Java 5.0
里出現兩種鎖的工具可供使用,下圖是這兩個工具的接口及其實現: