- 相關(guān)推薦
構(gòu)建多線程Java應(yīng)用程序
大多數(shù)服務(wù)端應(yīng)用程序都需要同時(shí)處理任務(wù)的能力,這樣可以提高工作性能并增加硬件資源的利用。在早期的Java版本(1.4或更早的)中,開(kāi)發(fā)者需要完成并發(fā)(concurrent)應(yīng)用程序——包括線程池邏輯—他們自己使用的是低層次語(yǔ)言結(jié)構(gòu)和Java Thread API.但是結(jié)果卻總是不理想。Java Thread API的特性會(huì)導(dǎo)致不知情的編程者開(kāi)發(fā)一些難以調(diào)試的編程錯(cuò)誤的代碼。
在Java5.0中,Sun公司采用了Java concurrency功能(JSR-166)來(lái)解決這些問(wèn)題,并且提供了一套標(biāo)準(zhǔn)的APIs來(lái)創(chuàng)建并開(kāi)發(fā)應(yīng)用程序。本文探究了一些Java concurrency package提供的特性并使用這些功能來(lái)演示編寫(xiě)并發(fā)應(yīng)用程序的技術(shù)。
Concurrent Programming的挑戰(zhàn)
自從它發(fā)布以來(lái),Java就提供了Thread類(lèi)和低層次語(yǔ)言結(jié)構(gòu),例如synchronized 和volatile用來(lái)開(kāi)發(fā)獨(dú)立平臺(tái)的并發(fā)應(yīng)用程序。但是,用這些特性來(lái)構(gòu)建并發(fā)應(yīng)用程序并不是簡(jiǎn)單的事情。開(kāi)發(fā)者要面對(duì)以下的挑戰(zhàn):
不正確的編程會(huì)導(dǎo)致一些困境,就是兩個(gè)或兩個(gè)以上的線程都等待永遠(yuǎn)被鎖住的對(duì)方。
在Java語(yǔ)言中沒(méi)有機(jī)制用于寫(xiě)wait-free, lock-free算法。開(kāi)發(fā)者必須使用本地代碼。
開(kāi)發(fā)者必須編寫(xiě)他們復(fù)雜的線程池邏輯,這樣會(huì)很棘手并且容易出錯(cuò)。
Java Concurrency Utilities的概述
JSR-166(Java concurrency utilities),是Java5.0的一部分,通過(guò)著重在寬度并提供跨域大范圍并發(fā)編程風(fēng)格的重要功能,大大簡(jiǎn)化了在Java中并發(fā)應(yīng)用程序的開(kāi)發(fā)。
Java concurrency utilities提供了多種功能,開(kāi)發(fā)者可以應(yīng)用于更快更有預(yù)見(jiàn)性的并發(fā)應(yīng)用程序的開(kāi)發(fā)。這些功能讓開(kāi)發(fā)者從通過(guò)寫(xiě)自定義代碼來(lái)重新發(fā)明wheel中解放出來(lái)。一些JSR-166的最顯著的特點(diǎn)是:
標(biāo)準(zhǔn)的接口和構(gòu)架來(lái)定義自定義線程子系統(tǒng)。
是一種機(jī)制用于規(guī)范調(diào)用,時(shí)序安排,執(zhí)行和異步任務(wù)的控制,這是根據(jù)在Executor構(gòu)架中的一套執(zhí)行政策。
是一種機(jī)制通過(guò)類(lèi)用于線程協(xié)調(diào),例如semaphore, mutexe, barrier, latche和 exchangers.
非阻礙FIFO列隊(duì)執(zhí)行(ConcurrentLinkedQueue類(lèi))用于可升級(jí)的,有效的線程安全操作。
阻礙列隊(duì)執(zhí)行類(lèi)來(lái)涵蓋最常見(jiàn)的使用情況,對(duì)于生成者/消費(fèi)者方法,信息,并行任務(wù)和相關(guān)的并發(fā)設(shè)計(jì)。
是一種構(gòu)架用于鎖定和等待不同于內(nèi)置同步和監(jiān)測(cè)器的條件。
隨時(shí)準(zhǔn)備使用的類(lèi)用于在單個(gè)變量上的鎖定自由,線程安全的編程。
開(kāi)發(fā)一個(gè)Concurrent Java Application
本節(jié)是演示如何使用Java concurrency utility API來(lái)開(kāi)發(fā)一個(gè)多線程的在線訂單程序的電子商務(wù)應(yīng)用程序。在應(yīng)用程序生效并授權(quán)命令之后,把它們放在訂單處理列隊(duì)(java.util.concurrent.BlockingQueue)。訂單處理器線程池不斷的對(duì)訂單進(jìn)行測(cè)驗(yàn),而且當(dāng)這些訂單可以使用時(shí)進(jìn)行處理。
解耦應(yīng)用程序的訂單處理代碼提供了增加和減少訂單處理率的靈活性,通過(guò)改變線程池的大小。在一個(gè)并發(fā)BlockingQueue中放入訂單對(duì)象確保一個(gè)處理器處理一個(gè)訂單,而且它要照顧自動(dòng)同步。
在以下小節(jié)中的代碼段是截取于伴隨本文中的應(yīng)用程序源代碼。
擴(kuò)展ThreadPoolExecutor
Executor接口只規(guī)定了一個(gè)方法并從任務(wù)如何運(yùn)行中解耦任務(wù)提交。ExecutorService子接口規(guī)定了額外的方法用于提交并追蹤異步任務(wù),以及關(guān)閉線程池。ThreadPoolExecutor類(lèi)是ExecutorService接口的一個(gè)具體的執(zhí)行,應(yīng)該足以用于大多數(shù)訂單處理應(yīng)用程序的需求。
ThreadPoolExecutor也提供有用的連接方法(e.g., beforeExecute),可以覆蓋定制目的。在Listing 1中的CustomThreadPoolExecutor類(lèi)擴(kuò)展了ThreadPoolExecutor類(lèi)并覆蓋了beforeExecute, afterExecute和 terminated 方法。
@Override
public void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); Logger.log("After calling afterExecute() method for a thread " + r); } @Override public void terminated() { super.terminated(); Logger.log("Threadpool terminated"); } |
該覆蓋方法只需登陸復(fù)寫(xiě)信息,但是在現(xiàn)實(shí)的情況中,它們能做更有用的事情。例如,terminated方法可以發(fā)送一個(gè)在這個(gè)池中的所有線程都死鎖的警告。要正確構(gòu)建多重覆蓋,通常你應(yīng)該從在子類(lèi)中的各個(gè)方法中調(diào)用主類(lèi)的覆蓋方法。
Java concurrency API還提供了ScheduledExecutorService接口,可以擴(kuò)展ExecutorServiceInterface,而且在一個(gè)特定延遲或是定期的執(zhí)行之后能夠安排任務(wù)進(jìn)行運(yùn)行。ScheduledExecutorThreadPool是這個(gè)接口的具體執(zhí)行。確定異步任務(wù)執(zhí)行
Executor 執(zhí)行提交異步任務(wù),這些任務(wù)執(zhí)行實(shí)際的業(yè)務(wù)邏輯。向executor提交一個(gè)任務(wù),ExecutorService接口提供重載的submit方法,可以接受Runnable 或是Callable 對(duì)象類(lèi)型。
Runnable任務(wù)類(lèi)型在以下情況下市非常有用的:
任務(wù)完成時(shí)不需要返回任何結(jié)果。
如果run()方法遇到一個(gè)例外,沒(méi)有必要拋出一個(gè)特定程序檢查例外。
移植現(xiàn)有的legacy 類(lèi)來(lái)實(shí)施Runnable接口是必需的。
Callable任務(wù)類(lèi)型提供了更多靈活性并提供了下列的優(yōu)點(diǎn):
任務(wù)可以返回用戶定義對(duì)象作為結(jié)果。
任務(wù)可以拋出用戶定義的檢查例外。
你需要分別為Runnable 和Callable任務(wù)類(lèi)型執(zhí)行run() 和call()方法。
在Listing 2中的OrderProcessorCallable類(lèi)執(zhí)行Callable接口并指定一個(gè)Integer作為結(jié)果對(duì)象。Constructor把任務(wù)對(duì)象名稱(chēng)和BlockingQueue當(dāng)做檢索命令來(lái)處理。call()方法繼續(xù)為訂單值對(duì)象調(diào)查BlockingQueue,并且處理任何所它所發(fā)現(xiàn)的事情。如果沒(méi)有訂單處理,call()方法會(huì)休息一段時(shí)間再繼續(xù)工作。
Call方法的無(wú)限循環(huán)在這個(gè)應(yīng)用程序方案中是非常有用的,因?yàn)橐驗(yàn)闆](méi)有必要一次又一次為每個(gè)訂單對(duì)象去創(chuàng)建并提交新的任務(wù)到ThreadPoolExecutor中。
public Integer call() throws OrderProcessingException {
while (running) { // check if current Thread is interrupted checkInterruptStatus(); // poll for OrderVO from blocking queue and do // order processing here …… } // return result return processedCount; } |
請(qǐng)注意異步任務(wù)執(zhí)行不斷的為應(yīng)用程序的生命周期運(yùn)行。在大多數(shù)程序中,異步任務(wù)執(zhí)行會(huì)進(jìn)行必要的操作并立即返回。
處理線程中斷
Call方法執(zhí)行使用checkInterruptStatus方法在執(zhí)行線程中斷上進(jìn)行經(jīng)常性檢查。這是必需的因?yàn)闉榱似仁谷蝿?wù)取消,ThreadPoolExecutor會(huì)向線程發(fā)送一個(gè)interrupt.否則檢查中斷狀態(tài)會(huì)導(dǎo)致特定線程再也無(wú)法返回。以下的checkInterruptStatus方法檢查運(yùn)行線程的中斷狀態(tài),如果線程被中斷會(huì)拋出OrderProcessingException.
private void checkInterruptStatus() throws
OrderProcessingException {
if (Thread.interrupted()) {
throw new OrderProcessingException("Thread was interrupted");
}
}
作為任務(wù)的實(shí)施,它是拋出一個(gè)異常并在運(yùn)行線程被中斷的時(shí)候終止任務(wù)執(zhí)行的一個(gè)非常好的練習(xí)。但是,基于訂單處理的應(yīng)用程序需求,在這種情況下忽略線程中斷是非常恰當(dāng)?shù)摹?/p>
【構(gòu)建多線程Java應(yīng)用程序】相關(guān)文章:
java多線程面試題201710-03
2016年java多線程面試題及答案07-02
sun認(rèn)證考試輔導(dǎo):java關(guān)于多線程的部分操作07-27
PHP Curl多線程原理詳解09-11
Java與Java web的區(qū)別08-10
sun認(rèn)證考試經(jīng)驗(yàn):多線程的幾種實(shí)現(xiàn)方法詳解07-24
未來(lái)郵件營(yíng)銷(xiāo)應(yīng)用程序的4個(gè)爆發(fā)點(diǎn)07-26
java習(xí)題及答案10-25