J2SE 5.0中的java.util.concurrent程序包提供了一個新的線程框架組件,這個框架組件處理了與建立、執(zhí)行和管理線程相關(guān)的很多低層細(xì)節(jié)信息。在本文中我們將細(xì)致地了解一下它的重要特性。
如果你使用C、C++或Java先前的版本進(jìn)行多線程編程,就知道在代碼中管理線程是多么頭疼的事情。在單線程程序中,代碼中引起應(yīng)用程序失敗的bug每次都在同一個點出現(xiàn)。但是在多線程程序中,只有某些原因遇到一起的時候才會出現(xiàn)失敗。由于預(yù)見可能引發(fā)應(yīng)用程序失敗的所有條件是非常困難的,所以多線程編程是有挑戰(zhàn)性的。有些程序員從根本上避免這種挑戰(zhàn),而另外一些--聰明的解決問題的人員--則一直坐在他們的計算機(jī)面前直到問題解決。
J2SE 5.0平臺包含了一個新的并發(fā)工具程序包。這個程序包中的類替并發(fā)類(concurrent classe)或并發(fā)設(shè)計中使用的應(yīng)用程序建立阻塞(blocking)。該并發(fā)工具包含下面一些內(nèi)容:
· 高性能的、靈活的線程池
· 異步執(zhí)行事務(wù)的框架組件
· 為并發(fā)訪問優(yōu)化過的集合類宿主(host)
本文介紹了J2SE 5.0框架組件類和它們的重要特性。本文的下載代碼提供了一些簡單的、容易使用的示例,它演示了所有的新線程框架組件類。你在閱讀文章內(nèi)容之后運(yùn)行這些示例可以使自己對這些特性有更好的理解。
Executor(執(zhí)行器)框架組件
Executor框架組件提供了一個簡單的、標(biāo)準(zhǔn)的、可擴(kuò)充的類,它提供了一些有用的功能,如果沒有這些功能,我們要手工實現(xiàn)這些它們,會覺得十分單調(diào)和困難。該框架組件使調(diào)用、調(diào)度和執(zhí)行的操作標(biāo)準(zhǔn)化了。它通過一組執(zhí)行策略為控制異步事務(wù)提供了支持。
Executor接口執(zhí)行已提交的可以運(yùn)行的事務(wù)。它提供了一條途徑,允許我們把事務(wù)提交從事務(wù)執(zhí)行機(jī)制中分離出來。程序員通常使用Executor代替顯式地(explicitly)建立線程。Executor接口也提供事務(wù)的同步和異步執(zhí)行。
對于同步執(zhí)行,使用下面的命令:
Class MySynExecutor implements Executor{
public void execute(Runnable r) {
r.run();
}
}
對于異步執(zhí)行,使用下面的命令:
Class MyASynExecutor implements Executor{
public void execute(Runnable r) {
new Thread(r).start();
}
}
ExecutorService(執(zhí)行器服務(wù))類
ExecutorService類為管理一個或多個異步事務(wù)的終止和跟蹤事務(wù)執(zhí)行的過程提供了方法。代碼下載中的MyExecutorService.java文件演示了管理事務(wù)終止的過程。它初始化了大小為三個的線程池,然后依次添加了線程。當(dāng)線程的數(shù)量達(dá)到線程池的大小限制時,它調(diào)用關(guān)閉(shutdown)方法。在調(diào)用shutdown()方法之后,這個線程池不再接受新事務(wù)的執(zhí)行。在等待十秒以后,該線程池調(diào)用shutDownNow()。這個方法會盡最大的努力來終止所有運(yùn)行中的事務(wù)。在示例中,應(yīng)用程序試圖終止運(yùn)行中的線程失敗了。
ScheduledExecutorService(調(diào)度執(zhí)行器服務(wù))
ScheduledExecutorService類是我的最喜歡的類。它對于調(diào)度那些周期性執(zhí)行的事務(wù)非常方便,而周期性執(zhí)行的事務(wù)對于清除工作(例如清除你的應(yīng)用程序建立的臨時文件等等)尤其有用。下載代碼中的MyScheduledExecutorService.java文件通過每五秒鐘發(fā)出"嘟嘟"一聲演示了調(diào)度的過程:
final Runnable beeper = new Runnable() {
public void run() { System.out.println("beep"); }
};
final ScheduledFuture beeperHandle =scheduler.scheduleAtFixedRate(beeper, 1, 5, SECONDS); Future和FutureTask
在Java的早期版本中,查詢運(yùn)行中的線程狀態(tài),以及使線程在執(zhí)行之后返回一個值是非常困難的。由于run(運(yùn)行)方法返回void,你必須編寫大量的代碼從線程中返回一個值。使用過這種方法的程序員肯定了解其痛苦的經(jīng)歷。
你可以使用Future接口或者FutureTask類從異步執(zhí)行的線程中得到一個返回值。Future接口提供了檢查計算過程是否完成、檢索計算結(jié)果或終止計算過程的一些方法。FutureTask類提供了Future接口方法的基本實現(xiàn)(implementation)。只有計算過程完成以后才能檢索結(jié)果;如果計算過程沒有完成,get方法會被阻塞(block)。
下載代碼中的MyStringReverser.java文件演示了FutureTask類的使用,并提供了一個容易理解的示例。它以每秒鐘一個字符的速度從后向前顯示提交的字符串,同時主線程檢測事務(wù)是否完成了:
while(!future.isDone()){
System.out.println("Task not yet completed.");
try{
Thread.currentThread().sleep(500);
}catch(InterruptedException ie){
System.out.println("Will check after 1/2 sec.");
}
}
在事務(wù)完成以后,就使用get方法從Future對象中檢索結(jié)果:
System.out.println("Here is result..."+future.get());
ThreadPoolExecutor(線程池執(zhí)行器)
有了ThreadPoolExecutor類之后你可以編寫自己的服務(wù)器了。這個類為配置和調(diào)整服務(wù)器提供了很多的特性,與很多大規(guī)模的企業(yè)級EJB服務(wù)器相似。下面是它的一些配置參數(shù):
· 核心和最大的線程池大小
通過把corePoolSize和maximumPoolSize設(shè)置為相同的值,你就可以建立一個大小固定的線程池了。通過把maximumPoolSize設(shè)置為一個極大的值(例如Integer.MAX_VALUE),你就可以允許線程池容納任意數(shù)量的并發(fā)事務(wù)了。
· 根據(jù)需要構(gòu)造
在默認(rèn)情況下,只有在新事務(wù)要求的時候,ThreadPoolExecutor才開始建立和啟動核心的線程,但是你可以使用prestartCoreThread或prestartAllCoreThreads動態(tài)地重載它。
· 保持活動的時間
如果線程池中當(dāng)前線程的數(shù)量超過了corePoolSize,那么這些超過的線程的空閑時間大于keepAliveTime的時候,它們就會被終止。
· 排隊
排隊遵循下面的規(guī)則:
· 如果正在運(yùn)行的線程數(shù)量少于corePoolSize,Executor總會添加新線程而不會排隊。
· 如果corePoolSize或更多數(shù)量的線程在運(yùn)行,Executor總會對請求進(jìn)行排隊而不會添加新線程。
· 如果某個請求不能參與排隊,就會建立新線程,除非線程數(shù)量超過了maximumPoolSize(在超過的情況下,該事務(wù)會被拒絕)。
· Hook方法
這個類提供了beforeExecute()和afterExecute() hook方法,它們分別在每個事務(wù)執(zhí)行之前和之后被調(diào)用。為了使用它們,你必須建立這個類的子類(因為這些方法是受保護(hù)的)。
下載代碼中的MyThreadPoolExecutor.java提供了一些監(jiān)視多種配置參數(shù)的詳細(xì)示例。你可以發(fā)現(xiàn)隨著每個事務(wù)的增加和完成,線程池和隊列大小在不斷變化。你可以修改代碼中的設(shè)置信息。 并發(fā)集合
JDK 1.5提供了下面一些集合實現(xiàn),它們是被設(shè)計為用于多線程環(huán)境的:
· ConcurrentHashMap
· CopyOnWriteArrayList
· CopyOnWriteArraySet
ConcurrentHashMap類為檢索和更新(update)可調(diào)整的預(yù)期的并發(fā)性提供了完整的線程安全的(thread-safe)并發(fā)性支持。CopyOnWriteArraySet是一組線程安全的變量集合,CopyOnArrayList是一個線程安全的數(shù)組列表(ArrayList)變量。在修改原始的數(shù)組或集合之前,它們中的每一個都會把下層的數(shù)組或集合復(fù)制一份。其結(jié)果是,讀取的速度很快,而更新的速度很慢。
并發(fā)集合類為Iterator(迭代子)提供快照式的數(shù)據(jù)(即使下層數(shù)據(jù)發(fā)生了改變,在Iterator中也不會反映出來)。
同步器(Synchronizer)
JDK 1.5還提供了一些高級類,例如Semaphore、CountDownLatch和CyclicBarrier,還有一個用于同步的Exchanger(交換器)類。本文沒有介紹這些類的詳細(xì)的分析和使用信息,因為理解它們需要一些理論背景。
擁有了這些新的類之后,你可以說服害怕多線程技術(shù)的技術(shù)上司開發(fā)多線程應(yīng)用程序了。Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=332695