Java5 多執行緒實踐
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
Java5增加了新的類庫併發集java.util.concurrent,該類庫為併發程式提供了豐富的API多執行緒程式設計在Java 5中更加容易,靈活。本文通過一個網路伺服器模型,來實踐Java5的多執行緒程式設計,該模型中使用了Java5中的執行緒池,阻塞佇列,可重入鎖等,還實踐了Callable, Future等介面,並使用了Java 5的另外一個新特性泛型。
本文將實現一個網路伺服器模型,一旦有客戶端連線到該伺服器,則啟動一個新執行緒為該連線服務,服務內容為往客戶端輸送一些字元資訊。一個典型的網路伺服器模型如下:
1. 建立監聽埠。
2. 發現有新連線,接受連線,啟動執行緒,執行服務執行緒。
3. 服務完畢,關閉執行緒。
這個模型在大部分情況下執行良好,但是需要頻繁的處理使用者請求而每次請求需要的服務又是簡短的時候,系統會將大量的時間花費線上程的建立銷燬。Java 5的執行緒池克服了這些缺點。通過對重用執行緒來執行多個任務,避免了頻繁執行緒的建立與銷燬開銷,使得伺服器的效能方面得到很大提高。因此,本文的網路伺服器模型將如下:
1. 建立監聽埠,建立執行緒池。
2. 發現有新連線,使用執行緒池來執行服務任務。
3. 服務完畢,釋放執行緒到執行緒池。
下面詳細介紹如何使用Java 5的concurrent包提供的API來實現該伺服器。
初始化包括建立執行緒池以及初始化監聽埠。建立執行緒池可以通過呼叫java.util.concurrent.Executors類裡的靜態方法newChahedThreadPool或是newFixedThreadPool來建立,也可以通過新建一個java.util.concurrent.ThreadPoolExecutor例項來執行任務。這裡我們採用newFixedThreadPool方法來建立執行緒池。
ExecutorService pool = Executors.newFixedThreadPool(10);
表示新建了一個執行緒池,執行緒池裡面有10個執行緒為任務佇列服務。
使用ServerSocket物件來初始化監聽埠。
|
當有新連線建立時,accept返回時,將服務任務提交給執行緒池執行。
|
這裡使用執行緒池物件來執行執行緒,減少了每次執行緒建立和銷燬的開銷。任務執行完畢,執行緒釋放到執行緒池。
服務執行緒ServiceThread維護一個count來記錄服務執行緒被呼叫的次數。每當服務任務被呼叫一次時,count的值自增1,因此ServiceThread提供一個increaseCount和getCount的方法,分別將count值自增1和取得該count值。由於可能多個執行緒存在競爭,同時訪問count,因此需要加鎖機制,在Java 5之前,我們只能使用synchronized來鎖定。Java 5中引入了效能更加粒度更細的重入鎖ReentrantLock。我們使用ReentrantLock保證程式碼執行緒安全。下面是具體程式碼:
|
服務執行緒在開始給客戶端列印一個歡迎資訊,
|
然後使用ExecutorService的submit方法提交一個Callable的任務,返回一個Future介面的引用。這種做法對費時的任務非常有效,submit任務之後可以繼續執行下面的程式碼,然後在適當的位置可以使用Future的get方法來獲取結果,如果這時候該方法已經執行完畢,則無需等待即可獲得結果,如果還在執行,則等待到執行完畢。
|
這裡使用了Java 5的另外一個新特性泛型,宣告TimeConsumingTask的時候使用了String做為型別引數。必須實現Callable介面的call函式,其作用類似與Runnable中的run函式,在call函式裡寫入要執行的程式碼,其返回值型別等同於在類宣告中傳入的型別值。在這段程式中,我們提交了一個Callable的任務,然後程式不會堵塞,而是繼續執行dos.write("let's do soemthing other".getBytes());當程式執行到String result = future.get()時如果call函式已經執行完畢,則取得返回值,如果還在執行,則等待其執行完畢。
伺服器端的完整實現程式碼如下:
|
執行服務端,客戶端只需使用telnet 127.0.0.1 19527 即可看到資訊如下: