JDK 5.0 concurrent學習(一)
整個的列出順序是在我學習concurrent javadoc的順序上,按照依賴關係排序,依賴其他JDK 5.0中類越少、越容易理解的排在越前面。
- TimeUnit
顧名思義,就是時間的單位。以前如果呼叫方法需要傳入時間引數,時間引數的單位都是通過javadoc說明的,比如說Thread.sleep(long),時間的引數就是毫秒,如果需要休眠一秒,則需要呼叫Thread.sleep(1000)。
JDK 5.0中加入了該類,為時間數字後面加上了個單位,如TimeUnit.MILLISECONDS是毫秒, TimeUnit.SECONDS是秒,等等。目前感覺上用處並不是太大,如果說原來某呼叫只需要一個引數,那現在就需要傳兩個引數,實現類也需要通過傳入的兩個引數進行轉換,反而麻煩了一些。稍微有些為了OO而OO的嫌疑:)
示例:
假設某方法的宣告是invoke(int time, TimeUnit timeUnit),那麼在理論上invoke(1, TimeUnit.SECONDS)和invoke(1000,TimeUnit.MILLISECONDS)是等價的,當然,如果實現中沒有做判斷,結果還是會不一樣。
- Callable
單方法介面,非常類似於Runnable,之所以存在該介面而不是用Runnable介面的原因就是:Callable可以有返回值,並可以丟擲checked異常。其方法宣告為:V call() throws Exception
通過javadoc可以看到,在concurrent包中的不少方法有method(Callable)和method(Runnable,T)這兩種形式,如ExecutorService.submit/FutureTask的構造方法等等。但我的感覺是如果存在著不少的這類方法,為什麼不通過一個簡單的包裝類來做Runnable和Callable之間的轉換,卻採用眾多的方法過載來實現?我個人認為如method(new CallableWrapper(Runnable,T))這種形式似乎更好一點。
- Future
其表現了一個非同步操作的結果,該類是POSA2中的Active Object的一個重要組成部分。
在非同步操作中,方法呼叫和方法執行是分開的,比如可以在時間點1時,A呼叫了B的一個方法去執行某項操作並立即返回,而該操作實際上在時間點2才執行完畢,那麼Future類就可以表現整個非同步操作的結果,如A呼叫B方法時返回的就是一個Future物件,可以通過呼叫future.isDone()來判斷該非同步操作是否完成,可以呼叫future.cancel(boolean)來取消尚未執行或中止正在執行的非同步操作,還可以通過future.get()來得到非同步操作的結果,如果非同步操作尚未完成,該方法會一直阻塞,知道該非同步操作完成。
目前我所接觸到的非同步操作大多是通過IOC來實現的。一般IOC都是在方法呼叫時或方法呼叫前註冊一個回撥鉤子,等到方法執行完畢,再通過這個回撥鉤子通知呼叫者。就用Cindy中一個簡單的例子來說明:
session.addSessionListener(new SessionAdapter() {
public void sessionEstablished(...) {
//do something
}
});
session.start();
//do something else
這種方式對於回撥控制的比較弱,什麼時候回撥,會在哪個執行緒內被回撥,應用無法控制。而Future表現的則是表現非同步操作的另一種方式,控制權掌握在呼叫者本身,如上面的程式碼用Future來改寫:
Future future = session.start();
if (!future.isDone())
//do something else
future.get(); //如果該非同步操作還未執行完畢,則會阻塞,直到執行完畢並返回結果
//do something
可以看到,這種方式所有的控制權都在於呼叫者本身,比如可以做到如果在1s內該操作還未執行完畢,則取消該操作等等。但是IOC的方式並不是沒有價值的,它擁有更大的吞吐量,可以看出,如果要得到非同步的結果,Future方式要麼是採用輪詢去判斷操作是否完成,要麼是採用阻塞等待的方式,不管是採用哪種方式,系統總體的吞吐量都會降低(儘管相對於同步呼叫,系統的總體吞吐量是上升了)。
我的觀點是:
吞吐量角度: 同步呼叫 < 非同步Future方式 < 非同步IOC方式
控制角度: 同步呼叫 > 非同步Future方式 > 非同步IOC方式
採用何種方式,要依據設計什麼樣的系統而定,嘿嘿,廢話:)