天天看點

java 多線程 逾時_【Java】Java多線程任務逾時結束的5種實作方法

方法一:使用Thread.join(long million)

(先講一下本人對join方法的了解,已了解此方法的可以略過)join方法可以這樣了解,在了解它之前,先解釋另一個常識,即目前線程(後面稱為目标線程,因為它是我們想使其逾時結束的目标任務)的建立及start的調用,一定是在另一個線程中進行的(最起碼是main線程,也可以是不同于main線程的其他線程),這裡我們假設為main線程,并且稱之為依賴線程,因為目标線程的建立是在他裡面執行的。介紹完這些常識就可以進一步解釋了,join的字面意思是,使目标線程加入到依賴線程中去,也可以了解為在依賴線程中等待目标線程一直執行直至結束(如果沒有設定逾時參數的話)。設定了逾時參數(假設為5秒)就會這樣執行,在依賴線程中調用了join之後,相當于告訴依賴線程,現在我要插入到你的線程中來,即兩個線程合二為一,相當于一個線程(如果不執行插入的話,那目标線程和依賴線程就是并行執行),而且目标線程是插在主線程前面,是以目标線程先執行,但你主線程隻需要等我5秒,5秒之後,不管我有沒有執行完畢,我們兩都分開,這時又會變成兩個并行執行的線程,而不是目标線程直接結束執行,這點很重要。

其實這個方法比較牽強,因為它主要作用是用來多個線程之間進行同步的。但因為它提供了這個帶參數的方法(是以這也給了我們一個更廣泛的思路,就是一般帶有逾時參數的方法我們都可以嘗試着用它來實作逾時結束任務),是以我們可以用它來實作。注意這裡的參數的機關是固定的毫秒,不同于接下來的帶機關的函數。具體用法請看示例:

public classJoinTest {public static voidmain(String[] args) {

Task task1= new Task("one", 4);

Task task2= new Task("two", 2);

Thread t1= newThread(task1);

Thread t2= newThread(task2);

t1.start();try{

t1.join(2000); //在主線程中等待t1執行2秒

} catch(InterruptedException e) {

System.out.println("t1 interrupted when waiting join");

e.printStackTrace();

}

t1.interrupt();//這裡很重要,一定要打斷t1,因為它已經執行了2秒。

t2.start();try{

t2.join(1000);

}catch(InterruptedException e) {

System.out.println("t2 interrupted when waiting join");

e.printStackTrace();

}

}

}class Task implementsRunnable {publicString name;private inttime;public Task(String s, intt) {

name=s;

time=t;

}public voidrun() {for (int i = 0; i < time; ++i) {

System.out.println("task " + name + " " + (i + 1) + " round");try{

Thread.sleep(1000);

}catch(InterruptedException e) {

System.out.println(name+ "is interrupted when calculating, will stop...");return; //注意這裡如果不return的話,線程還會繼續執行,是以任務逾時後在這裡處理結果然後傳回

}

}

}

}

在主線程中等待t1執行2秒之後,要interrupt(而不是直接調用stop,這個方法已經被棄用)掉它,然後在t1裡面會産出一個中斷異常,在異常裡面處理完該處理的事,就要return,一定要return,如果不return的話,t1還會繼續執行,隻不過是與主線程并行執行。

方法二:Future.get(long million, TimeUnit unit) 配合Future.cancle(true)

Future系列(它的子類)的都可以實作,這裡采用最簡單的Future接口實作。

public classFutureTest {static class Task implements Callable{publicString name;private inttime;public Task(String s, intt) {

name=s;

time=t;

}

@Overridepublic Boolean call() throwsException {for (int i = 0; i < time; ++i) {

System.out.println("task " + name + " round " + (i + 1));try{

Thread.sleep(1000);

}catch(InterruptedException e) {

System.out.println(name+ " is interrupted when calculating, will stop...");return false; //注意這裡如果不return的話,線程還會繼續執行,是以任務逾時後在這裡處理結果然後傳回

}

}return true;

}

}public static voidmain(String[] args) {

ExecutorService executor=Executors.newCachedThreadPool();

Task task1= new Task("one", 5);

Future f1 =executor.submit(task1);try{if (f1.get(2, TimeUnit.SECONDS)) { //future将在2秒之後取結果

System.out.println("one complete successfully");

}

}catch(InterruptedException e) {

System.out.println("future在睡着時被打斷");

executor.shutdownNow();

}catch(ExecutionException e) {

System.out.println("future在嘗試取得任務結果時出錯");

executor.shutdownNow();

}catch(TimeoutException e) {

System.out.println("future時間逾時");

f1.cancel(true);//executor.shutdownNow();//executor.shutdown();

} finally{

executor.shutdownNow();

}

}

}

運作結果如下,task在2秒之後停止:

java 多線程 逾時_【Java】Java多線程任務逾時結束的5種實作方法

如果把Task中捕獲InterruptedException的catch塊中的return注釋掉,就是這樣的結果:

java 多線程 逾時_【Java】Java多線程任務逾時結束的5種實作方法

task繼續執行,直至結束

方法三:ExecutorService.awaitTermination(long million, TimeUnit unit)

這個方法會一直等待所有的任務都結束,或者逾時時間到立即傳回,若所有任務都完成則傳回true,否則傳回false

public classAwaitTermination {static class Task implementsRunnable {publicString name;private inttime;public Task(String s, intt) {

name=s;

time=t;

}public voidrun() {for (int i = 0; i < time; ++i) {try{

Thread.sleep(1000);

}catch(InterruptedException e) {

System.out.println(name+ " is interrupted when calculating, will stop...");return; //注意這裡如果不return的話,線程還會繼續執行,是以任務逾時後在這裡處理結果然後傳回

}

System.out.println("task " + name + " " + (i + 1) + " round");

}

System.out.println("task " + name + " finished successfully");

}

}public static voidmain(String[] args) {

ExecutorService executor=Executors.newCachedThreadPool();

Task task= new Task("one", 5);

Task task2= new Task("two", 2);

Future> future =executor.submit(task);

Future> future2 =executor.submit(task2);

List> futures = new ArrayList>();

futures.add(future);

futures.add(future2);try{if (executor.awaitTermination(3, TimeUnit.SECONDS)) {

System.out.println("task finished");

}else{

System.out.println("task time out,will terminate");for (Future>f : futures) {if (!f.isDone()) {

f.cancel(true);

}

}

}

}catch(InterruptedException e) {

System.out.println("executor is interrupted");

}finally{

executor.shutdown();

}

}

}

運作結果如下:

java 多線程 逾時_【Java】Java多線程任務逾時結束的5種實作方法

方法四:設定一個守護線程,守護線程先sleep一段定時時間,睡醒後打斷它所監視的線程

public classDemonThread {static class Task implementsRunnable {privateString name;private inttime;public Task(String s, intt) {

name=s;

time=t;

}public intgetTime() {returntime;

}public voidrun() {for (int i = 0; i < time; ++i) {try{

Thread.sleep(1000);

}catch(InterruptedException e) {

System.out.println(name+ " is interrupted when calculating, will stop...");return; //注意這裡如果不return的話,線程還會繼續執行,是以任務逾時後在這裡處理結果然後傳回

}

System.out.println("task " + name + " " + (i + 1) + " round");

}

System.out.println("task " + name + " finished successfully");

}

}static class Daemon implementsRunnable {

List tasks = new ArrayList();privateThread thread;private inttime;public Daemon(Thread r, intt) {

thread=r;

time=t;

}public voidaddTask(Runnable r) {

tasks.add(r);

}

@Overridepublic voidrun() {while (true) {try{

Thread.sleep(time* 1000);

}catch(InterruptedException e) {

e.printStackTrace();

}

thread.interrupt();

}

}

}public static voidmain(String[] args) {

Task task1= new Task("one", 5);

Thread t1= newThread(task1);

Daemon daemon= new Daemon(t1, 3);

Thread daemoThread= newThread(daemon);

daemoThread.setDaemon(true);

t1.start();

daemoThread.start();

}

}

一開始準備在守護任務裡面用一個集合來實作監視多個任務,接着發現要實作這個功能還得在這個守護任務裡面為每一個監視的任務開啟一個監視任務,一時又想不到更好的方法來解決,索性隻監視一個算了,留待以後改進吧。

運作結果如下:

java 多線程 逾時_【Java】Java多線程任務逾時結束的5種實作方法

方法五:使用Timer / TimerTask,或其他schedule定時相關的類

總結:需要注意的是,無論以上哪一種方法,其實作原理都是在逾時後通過interrupt打斷目标線程的運作,是以都要在捕捉到InterruptedException的catch代碼塊中return,否則線程仍然會繼續執行。另外,最後兩種方法本質上是一樣的,都是通過持有目标線程的引用,在定時結束後打斷目标線程,這兩種方法的控制精度最低,因為它是采用另一個線程來監視目标線程的運作時間,因為線程排程的不确定性,另一個線程在定時結束後不一定會馬上得到執行而打斷目标線程。