java多線程實現任務超時監聽


在實際的開發過程當中,會遇到這樣的需求:某些功能為了防止系統掛死,需要進行時間控制,超過一定的執行時間,就提示任務執行超時,不再繼續執行該任務,從而保證系統健壯性和穩定性。其實仔細想想,我們可以把這樣的需求,全部歸結為一種“超時控制的業務模型”,建立起自己熟悉的業務模型,以后碰到類似的需求,可以借鑒此方案。若有機會設計或重構系統,在必要的模塊中,也可以將該方案作為增強系統穩定性的一個備選方案。
方案一:使用守護線程
此方案需要的類有:
TimeoutThread:定義超時的線程,里面包含超時時間和是否執行完成狀態定義。主要任務就是監聽時間,超出時間后拋出運行時異常。
TimeoutException:定義的一個運行時異常,繼承RuntimeException。如果覺得名字有重復的話,也可以換成別的名字。
TaskThread:定義任務執行的線程,守護線程,包含成員變量TimeoutThread,里面主要執行任務主體,任務執行完后,修改TimeoutThread的狀態為執行完成。
MyUncauhtExceptionHandler:自定義的線程異常捕獲類,在守護進程由於超時被迫銷毀時,能夠執行這個異常里的代碼,一般用於任務執行主體超時后的狀態改變,如將任務標記為超時狀態。各位請注意:線程中拋出的異常,是不能夠被直接捕獲的。
MyHandlerThreadFactory(可選):實現ThreadFactory接口,線程的創建工廠,在這里主要是為線程池修改默認為線程異常捕獲工廠,若在代碼中設定Thread.setDefaultUncaughtExceptionHandler(new
MyUncauhtExceptionHandler());,則該類可以不用,但一般寫法需要用到該類。建議使用該類

示例代碼如下:

public class TimeoutThread implements Runnable {

	private long timeout;
	private boolean isCancel;
	
	public TimeoutThread(long timeout) {
		super();
		this.timeout = timeout;
	}

	/**
	 * 設定監聽是否取消
	 */
	public void isCancel() {
		this.isCancel = true;
	}
	
	@Override
	public void run() {
		try {
			Thread.sleep(timeout);
			if(!isCancel) {
				throw new TimeoutException("thread is timeout");
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}
public class TimeoutException extends RuntimeException {

	private static final long serialVersionUID = 551822143131900511L;

	
	/**
	 * 拋出異常信息
	 * @param str 異常信息
	 */
	public TimeoutException(String str) {
		super(str);
	}
	
}
public class TaskThread extends Thread{

	private TimeoutThread tt;
	
	/**
	 * 需要注入TimeoutThread對象
	 * 可根據不同的場景,注入不同的對象,完成任務的執行
	 * @param tt
	 */
	public TaskThread(TimeoutThread tt) {
		this.setDaemon(true);
		this.tt = tt;
	}
	
	@Override
	public void run() {
		try {
			//這里是任務的執行主體,為了簡單示例,只用sleep方法演示
			Thread.sleep(1000);
			//執行任務完成后,更改狀態
			tt.isCancel();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}

}
public class MyUncauhtExceptionHandler implements Thread.UncaughtExceptionHandler {

	@Override
	public void uncaughtException(Thread t, Throwable e) {
		//簡單起見,只打印一句話
		System.out.println("hehe");
	}

}
public class MyHandlerFactory implements ThreadFactory {

	@Override
	public Thread newThread(Runnable r) {
		//定義線程創建工廠,這里主要設定MyUncauhtExceptionHandler
		Thread t = new Thread(r);
		t.setUncaughtExceptionHandler(new MyUncauhtExceptionHandler());
		return t;
	}

}
public class Client {

	public static void main(String[] args) {
		//方式一:直接設定DefaultUncaughtExceptionHandler,然后直接t.start();task.start()啟動線程即可。
		Thread.setDefaultUncaughtExceptionHandler(new MyUncauhtExceptionHandler());
		//方式二:創建線程創建工廠,利用線程池啟動線程
		ExecutorService exec = Executors.newCachedThreadPool(new MyHandlerFactory());
		TimeoutThread tt = new TimeoutThread(800);
		Thread t = new Thread(tt);
		TaskThread task = new TaskThread(tt);
		exec.execute(t);
		exec.execute(task);
		exec.shutdown();
	}
}

方案二:使用Future的特性(推薦) 利用Future.get(long timeout,   TimeUnit unit)方法。 1、新建TaskThread類,實現Callable接口,實現call()方法。 2、線程池調用submit()方法,得到Future對象。 3、調用Future對象的get(long timeout,   TimeUnit unit)方法,該方法的特點:阻塞式線程調用,同時指定了超時時間timeout,get方法執行超時會拋出timeout異常,該異常需要捕獲。 示例代碼:

public class TimeTask implements Callable<String> {

	@Override
	public String call() throws Exception {
		//執行任務主體,簡單示例
		Thread.sleep(1000);
		return "hehe";
	}

}
ExecutorService exec = Executors.newCachedThreadPool();
		Future<String> f = exec.submit(new TimeTask());
		try {
			f.get(200, TimeUnit.MILLISECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			//定義超時后的狀態修改
			System.out.println("thread time out");
			e.printStackTrace();
		}

方案總結:        花了比較多的篇幅介紹方案一,主要目的是了解java 線程的基本處理方案機制,守護線程的特性,線程異常的處理方法。可以通過這個方案,多了解一些java的基礎知識。個人建議不推薦在企業應用開發中使用此方案。        方案二利用現有的Future屬性,在開發過程中直接利用JDK的方法,省時省力,並且可靠性高。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM