在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
這邊特別來說明一下,Generic Type(泛型)這個技巧。

定義泛型

[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;  
     }  
    });  
 }  

參考資料

  1. Generic Types, http://docs.oracle.com/javase/tutorial/java/generics/types.html
  2. http://tutorials.jenkov.com/java-concurrency/creating-and-starting-threads.html
  3. http://www.javaworld.com.tw/jute/post/view?bid=5&id=105545&sty=3
  4. http://jjnnykimo.pixnet.net/blog/post/21585257-%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3abstract-class%E5%92%8Cinterface




留言

這個網誌中的熱門文章

如何關閉nouveau-kernel-driver,解決無法安裝Nvidia driver問題

如何在Nginx所設置的Proxy中可以取得真實客戶的IP位址

如何客製VMware ESXi安裝光碟(加入Realtek驅動程式)