2012年1月11日 星期三

Java 定時執行 (2):將 ExecutorService 產生的 Thread 加入 ThreadGroup

前一篇文章提到,我選用 ExecutorService 的主因是比較好控制 Thread
而控制的方法是把要產生的 Thread 都加入到我指定的一個 ThreadGroup
如此一來我只要用 ThreadGroup.activeCount() 就可以知道定時執行產生的 Thread 還有幾個正在執行
也可以用 Thread.enumerate(new Thread[ ]) 把 ThreadGroup 中的 Thread 列舉出來。

參考資料:How to use ThreadFactory in Java

產生 ExecutorService 的程式碼如下:

// Initial a thread group
ThreadGroup group = new ThreadGroup("GroupName");
// Initail a ExecutorService with ThreadFactory
ExecutorService executor = Executors.newSingleThreadExecutor(new MyThreadFactory(group, "ThreadName"));

其中 ThreadFactory 如下:

import java.util.concurrent.ThreadFactory;

public class MyThreadFactory implements ThreadFactory {
  private int count;
  private String prefix;
  private ThreadGroup group;
  
  public MyThreadFactory (ThreadGroup group, String prefixString) {
    this.count = 0;
    this.prefix = prefixString;
    this.group = group;
  }
  
  @Override
  public Thread newThread(Runnable r) {
    return new Thread(this.group, r, this.prefix + "-" + this.count++);
  }
}

如此一來,ThreadFactory 就可以依照我希望的
把 ExecutorService 產生的 Thread 產生在我指定的 ThreadGroup 裡,方便程式日後的存取和監控。

另外需要注意的是,目前有發現如果是 web application 的話
ExecutorService 要記得在網站關閉但伺服器不關閉的時候(例如重新佈署網站)把 ExecutorService 關掉
否則 ExecutorService 產生的 Thread 預設是不會在 web application 被 un-deploy 時自動關閉,會產生 memory leak 的問題。
關閉 ExecutorService 的語法是:

executor.shutDownNow();

而要關閉 ThreadGroup 內的 Thread 時,似乎直接呼叫 ThreadGroup 的 interrupt() 無效?
我的解法是用 enumerate() 列舉出 ThreadGroup 內的所有 Thread
再用迴圈一個一個呼叫它的 interrupt()
不過當然程式中必須隨時要有檢查是否 interrupt 的確認
Java 中 Thread 的 InterruptException 只會發生在 wait 狀態
其他狀態中只是 isInterrupt 會被設為 true,但 Thread 並不會馬上就中斷
要依靠程式碼自行跳脫到 run() 的最底端,讓 Thread 自然結束。

有關如何安全關閉 Thread,可以參考:How to Stop a Thread or a Task

沒有留言: