當前位置:才華齋>IT認證>JAVA認證>

構建多執行緒Java應用程式

JAVA認證 閱讀(1.48W)

大多數服務端應用程式都需要同時處理任務的能力,這樣可以提高工作效能並增加硬體資源的利用。在早期的Java版本(1.4或更早的)中,開發者需要完成併發(concurrent)應用程式——包括執行緒池邏輯—他們自己使用的是低層次語言結構和Java Thread API.但是結果卻總是不理想。Java Thread API的特性會導致不知情的程式設計者開發一些難以除錯的程式設計錯誤的程式碼。

構建多執行緒Java應用程式

在Java5.0中,Sun公司採用了Java concurrency功能(JSR-166)來解決這些問題,並且提供了一套標準的APIs來建立並開發應用程式。本文探究了一些Java concurrency package提供的特性並使用這些功能來演示編寫併發應用程式的技術

 Concurrent Programming的挑戰

自從它釋出以來,Java就提供了Thread類和低層次語言結構,例如synchronized 和volatile用來開發獨立平臺的併發應用程式。但是,用這些特性來構建併發應用程式並不是簡單的事情。開發者要面對以下的挑戰:

不正確的程式設計會導致一些困境,就是兩個或兩個以上的執行緒都等待永遠被鎖住的對方。

在Java語言中沒有機制用於寫wait-free, lock-free演算法。開發者必須使用原生代碼。

開發者必須編寫他們複雜的執行緒池邏輯,這樣會很棘手並且容易出錯。

 Java Concurrency Utilities的概述

JSR-166(Java concurrency utilities),是Java5.0的一部分,通過著重在寬度並提供跨域大範圍併發程式設計風格的重要功能,大大簡化了在Java中併發應用程式的開發。

Java concurrency utilities提供了多種功能,開發者可以應用於更快更有預見性的併發應用程式的開發。這些功能讓開發者從通過寫自定義程式碼來重新發明wheel中解放出來。一些JSR-166的最顯著的特點是:

標準的介面和構架來定義自定義執行緒子系統。

是一種機制用於規範呼叫,時序安排,執行和非同步任務的控制,這是根據在Executor構架中的一套執行政策。

是一種機制通過類用於執行緒協調,例如semaphore, mutexe, barrier, latche和 exchangers.

非阻礙FIFO列隊執行(ConcurrentLinkedQueue類)用於可升級的,有效的執行緒安全操作。

阻礙列隊執行類來涵蓋最常見的使用情況,對於生成者/消費者方法,資訊,並行任務和相關的.併發設計。

是一種構架用於鎖定和等待不同於內建同步和監測器的條件。

隨時準備使用的類用於在單個變數上的鎖定自由,執行緒安全的程式設計。

 開發一個Concurrent Java Application

本節是演示如何使用Java concurrency utility API來開發一個多執行緒的線上訂單程式的電子商務應用程式。在應用程式生效並授權命令之後,把它們放在訂單處理列隊(kingQueue)。訂單處理器執行緒池不斷的對訂單進行測驗,而且當這些訂單可以使用時進行處理。

解耦應用程式的訂單處理程式碼提供了增加和減少訂單處理率的靈活性,通過改變執行緒池的大小。在一個併發BlockingQueue中放入訂單物件確保一個處理器處理一個訂單,而且它要照顧自動同步。

在以下小節中的程式碼段是擷取於伴隨本文中的應用程式原始碼。

  擴充套件ThreadPoolExecutor

Executor介面只規定了一個方法並從任務如何執行中解耦任務提交。ExecutorService子介面規定了額外的方法用於提交併追蹤非同步任務,以及關閉執行緒池。ThreadPoolExecutor類是ExecutorService介面的一個具體的執行,應該足以用於大多數訂單處理應用程式的需求。

ThreadPoolExecutor也提供有用的連線方法(e.g., beforeExecute),可以覆蓋定製目的。在Listing 1中的CustomThreadPoolExecutor類擴充套件了ThreadPoolExecutor類並覆蓋了beforeExecute, afterExecute和 terminated 方法。

  @Override

public void afterExecute(Runnable r, Throwable t) {

rExecute(r, t);

("After calling afterExecute() method for a thread "

+ r);

}

@Override

public void terminated() {

inated();

("Threadpool terminated");

}

該覆蓋方法只需登陸複寫資訊,但是在現實的情況中,它們能做更有用的事情。例如,terminated方法可以傳送一個在這個池中的所有執行緒都死鎖的警告。要正確構建多重覆蓋,通常你應該從在子類中的各個方法中呼叫主類的覆蓋方法。

Java concurrency API還提供了ScheduledExecutorService介面,可以擴充套件ExecutorServiceInterface,而且在一個特定延遲或是定期的執行之後能夠安排任務進行執行。ScheduledExecutorThreadPool是這個介面的具體執行。確定非同步任務執行

Executor 執行提交非同步任務,這些任務執行實際的業務邏輯。向executor提交一個任務,ExecutorService介面提供過載的submit方法,可以接受Runnable 或是Callable 物件型別。

Runnable任務型別在以下情況下市非常有用的:

任務完成時不需要返回任何結果。

如果run()方法遇到一個例外,沒有必要丟擲一個特定程式檢查例外。

移植現有的legacy 類來實施Runnable介面是必需的。

Callable任務型別提供了更多靈活性並提供了下列的優點:

任務可以返回使用者定義物件作為結果。

任務可以丟擲使用者定義的檢查例外。

你需要分別為Runnable 和Callable任務型別執行run() 和call()方法。

在Listing 2中的OrderProcessorCallable類執行Callable介面並指定一個Integer作為結果物件。Constructor把任務物件名稱和BlockingQueue當做檢索命令來處理。call()方法繼續為訂單值物件調查BlockingQueue,並且處理任何所它所發現的事情。如果沒有訂單處理,call()方法會休息一段時間再繼續工作。

Call方法的無限迴圈在這個應用程式方案中是非常有用的,因為因為沒有必要一次又一次為每個訂單物件去建立並提交新的任務到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;

}

請注意非同步任務執行不斷的為應用程式的生命週期執行。在大多數程式中,非同步任務執行會進行必要的操作並立即返回。

處理執行緒中斷

Call方法執行使用checkInterruptStatus方法在執行執行緒中斷上進行經常性檢查。這是必需的因為為了迫使任務取消,ThreadPoolExecutor會向執行緒傳送一個interrupt.否則檢查中斷狀態會導致特定執行緒再也無法返回。以下的checkInterruptStatus方法檢查執行執行緒的中斷狀態,如果執行緒被中斷會丟擲OrderProcessingException.

private void checkInterruptStatus() throws

OrderProcessingException {

if (rrupted()) {

throw new OrderProcessingException("Thread was interrupted");

}

}

作為任務的實施,它是丟擲一個異常並在執行執行緒被中斷的時候終止任務執行的一個非常好的練習。但是,基於訂單處理的應用程式需求,在這種情況下忽略執行緒中斷是非常恰當的。