在Java中使用Thread的小技巧
遇到問題
在Java程式中,如果遇到需要將程式平行處理,通常會馬上想到使用Java thread來解決問題。但如果可以進一步來將問題研究一下,或許可以產生更有效率的程式碼,並且可以重複使用。假設,目前程式處理一個List內每個item資料的行為一致,並依序處理每個item,一個接一個處理,完整處理完後在接著下一個,這樣的最大處理時間會是N(Item數目) * T(每個Item處理時間)。如果使用平行化的模式,就可以將List內的每個item處理,分散到每個獨立的thread來處理,如此ㄧ來便可加速處理,處理的理想時間會是T(每個Item處理時間) + X(overhead time)。
所以,我們會有一個List,一個處理的方法,然後,平行化。直覺的方法,使用個迴圈,加上Java thread就可以輕易使用thread來達到平行化的處理。這樣很好,然有沒有辦法可以,重複使用這樣的程式碼呢? 如果你的程式會有多個地方都需要做這樣的處理,你得不斷的重複著相同且類似的程式碼。
這邊來介紹個技巧,使用Java Generic Type、Interface和Thread來解決這樣的問題。
使用的技巧
- Generic Type
- Interface
- Thread
定義泛型
[class-modifiers] class GenericsName<T1 [, T2, T3, ...]> { ...... }
泛型由泛型名稱(generic type name)、角括號 "<>"、包含在角括號內的型態參數(type parameter)組成。在角括號 "<>" 內的型態參數( Tx ) 須為某種參考型態。
建立泛型物件(Instantiating a Generic Type)
建立泛型物件須使用 new 關鍵字,並指定型態參數的值(values of the type parameters)。此種指定值的型態參數稱為 parameterized type。
範例說明
class MyHashTable<Key, Value> { // MyHashtable 稱為 generic type name,Key 及 Value 為 type parameter
......
Value put(Key k, Value v) {...}
Value get(Key k) {...}
}
class Demo {
public static void main(String[] args) {
MyHashTable<Integer, String> h = new MyHashTable<Integer, String>( );
h.put(new Integer(0), "value");
......
}
}
更詳細的說明可以參考[3]。
範例程式
這個範例程式會建立一個class,ThreadWorker。ThreadWorker主要的部分,兩個型態參數(T1,T2),兩個Interface(Threader4OneParams,Threader4TwoParams),還有方法WaitForProcesses()。WaitForProcessess這個方法主要定義,將List中每個item拿出來,並且開啟一個Java thread來處理這個item。如何處理,這個則由實際implement,Threader4OneParams或是Threader4TwoParams這兩個threader的run方法中定義。我們先從main()來看,如何使用這樣的方式。首先,先建立ThreadWorker實體,並指定型態參數的值為<String, Object>,然後,呼叫ThreadWoker的方法WaitForProcesses,這個方法要兩個參數,一個就是處理目標List,另一個就是Threader。這邊的Threder在ThreadWorker為兩個Interface,並定義了run這個方法。run這個方法的實作,只需要在Threader在實體化的時候,在實作需要執行的內容。這樣一來就解決所"遇到問題"了。
public class ThreadWorker<T1, T2> {
public interface Threader4OneParams<T1> {
public boolean run(T1 t1);
}
public interface Threader4TwoParams<T1, T2> {
public boolean run(T1 t1, T2 t2);
}
class ThreadCounter {
private int _value = 0;
public int tick() {
return ++this._value;
}
}
public boolean WaitForProcesses(final List<T1> list, final Threader4OneParams<T1> threader) {
if (list.size() <= 0) {
return true;
}
final Object mutex = new Object();
final ThreadCounter threadCount = new ThreadCounter();
final BoolReturn boolReturn = new BoolReturn(true);
for (final T1 elem : list) {
new Thread(new Runnable() {
public void run() {
try {
boolean rtn = threader.run(elem);
if (!rtn) {
boolReturn.setValue(false);
}
} catch (Exception e) {
boolReturn.setValue(false);
e.printStackTrace();
} finally {
synchronized (mutex) {
if (threadCounter.tick() >= list.size()) {
mutex.notifyAll();
}
}
}
}
}).start();
}
synchronized (mutex) {
try {
mutex.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return boolReturn.getValue();
}
public boolean WaitForProcesses(final Map<T1, T2> map, final Threader4TwoParams<T1, T2> processStart){}
}
public static void main(String[] args) throws IOException, InterruptedException {
boolean result = (new ThreadWorker<String, Object>()).WaitForProcesses(
ObjectValueList,
new ThreadWorker.Threader4OneParams<String>() {
public boolean run(String objectValue) {
// ------------------------
// something you want to do
// ------------------------
return true;
}
});
}
參考資料
- Generic Types, http://docs.oracle.com/javase/tutorial/java/generics/types.html
- http://tutorials.jenkov.com/java-concurrency/creating-and-starting-threads.html
- http://www.javaworld.com.tw/jute/post/view?bid=5&id=105545&sty=3
- http://jjnnykimo.pixnet.net/blog/post/21585257-%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3abstract-class%E5%92%8Cinterface
留言
張貼留言