標籤:manager java 針對性
雲智慧(北京)科技有限公司 陳鑫
情境
一個調度器,兩個調度任務,分別處理兩個目錄下的txt檔案,某個調度任務應對某些複雜問題的時候會持續特別長的時間,甚至有一直阻塞的可能。我們需要一個manager來管理這些task,當這個task的上一次執行時間距離現在超過5個調度周期的時候,就直接停掉這個線程,然後再重啟它,保證兩個目標目錄下沒有待處理的txt檔案堆積。
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/6F/6D/wKioL1WcivLD5mRPAAFx2PRJt5Q339.jpg" style="float:none;" title="1.png" alt="wKioL1WcivLD5mRPAAFx2PRJt5Q339.jpg" />
問題
直接使用java預設的線程池調度task1和task2.由於外部txt的種種不可控原因,導致task2線程阻塞。現象就是task1和線程池調度器都正常運行著,但是task2遲遲沒有動作。
當然,找到具體的阻塞原因並進行針對性解決是很重要的。但是,這種措施很可能並不能完全、徹底、全面的處理好所有未知情況。我們需要保證任務線程或者調度器的健壯性!
方案計劃
線程池調度器並沒有原生的針對被調度線程的業務運行狀態進行監控處理的API。因為task2是阻塞在我們的商務邏輯裡的,所以最好的方式是寫一個TaskManager,所有的任務線程在執行任務前全部到這個TaskManager這裡來註冊自己。這個TaskManager就負責對於每個自己管轄範圍內的task進行即時全程監控!
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/6F/70/wKiom1WciSOyqm2tAAHWv6lwMzY510.jpg" title="2.png" style="float:none;" alt="wKiom1WciSOyqm2tAAHWv6lwMzY510.jpg" />
後面的重點就是如何處理超過5個執行循環的task了。
方案如下:
一旦發現這個task線程,立即中止它,然後再次重啟;
一旦發現這個task線程,直接將整個pool清空並停止,重新放入這兩個task ——【task明確的情況下】;
方案實施
中止後重啟
Task實作類別
classFileTask extends Thread {
private long lastExecTime = 0;
protected long interval = 10000;
public long getLastExecTime() {
returnlastExecTime;
}
public void setLastExecTime(longlastExecTime) {
this.lastExecTime =lastExecTime;
}
public long getInterval() {
return interval;
}
public void setInterval(long interval) {
this.interval = interval;
}
public File[] getFiles() {
return null;
}
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()) {
lastExecTime = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + " is running ->" + new Date());
try {
Thread.sleep(getInterval() * 6 * 1000);
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace(); // 當線程池shutdown之後,這裡就會拋出exception了
}
}
}
}
TaskManager
public class TaskManager implements Runnable {
private final static Log logger = LogFactory.getLog(TaskManager.class);
public Set<FileTask> runners = newCopyOnWriteArraySet<FileTask>();
ExecutorService pool =Executors.newCachedThreadPool();
public voidregisterCodeRunnable(FileTask process) {
runners.add(process);
}
publicTaskManager (Set<FileTask>runners) {
this.runners = runners;
}
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()) {
try {
long current = System.currentTimeMillis();
for (FileTask wrapper : runners) {
if (current - wrapper.getLastExecTime() >wrapper.getInterval()* 5) {
wrapper.interrupt();
for (File file : wrapper.getFiles()) {
file.delete();
}
wrapper.start();
}
}
} catch(Exception e1) {
logger.error("Error happens when we trying to interrupt and restart a task");
ExceptionCollector.registerException(e1);
}
try {
Thread.sleep(500);
} catch(InterruptedException e) {
}
}
}
}
這段代碼會報錯java.lang.Thread IllegalThreadStateException。為什麼呢?其實這是一個很基礎的問題,您應該不會像我一樣馬虎。查看Thread.start()的注釋, 有這樣一段:
It is never legal to start a thread more thanonce. In particular, a thread may not be restarted once it has completedexecution.
java使用預設線程池踩過的坑(一)