計劃任務之ScheduledExecutorService
在講ScheduledExecutorService之前先來看看另外一個定時任務Timer的使用
定時任務TimerTimer類能夠設定定時任務的相應方法不多,並且不夠靈活,在高併發場景下效率不高。
這裡以週期執行任務方法為例來看Timer內部是如何工作的
示例:
public class TimerDemo { public static void main(String[] args) { Timer timer = new Timer() ; timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName()) ; } }, new Date(), 2000); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ", task...") ; } }, new Date(), 2000); } }
這裡很簡單添加了2個週期任務都是每2秒輸出當前執行緒的名稱資訊。
從控制檯也能看出來,發現2個任務都是同一個執行緒來完成排程的。
在Timer類中有個成員變數:
這個類繼承了Thread類。
new Timer時設定執行緒名稱並啟動執行緒:
啟動執行緒後執行run方法:
mainLoop方法:
該方法死迴圈不停的從queue中獲取任務執行。
當Timer執行時出現異常時會發生什麼呢?
public class TimerDemo { static int a = 10 ; public static void main(String[] args) { Timer timer = new Timer() ; timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { if (a == 12) { throw new RuntimeException("發生錯誤") ; } a++ ; System.out.println(Thread.currentThread().getName()) ; } }, new Date(), 2000); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ", task...") ; } }, new Date(), 2000); }}
執行結果:
1、當發生異常時整個Timer就停止了,這肯定不是我們想要的。
2、Timer對排程的支援是基於絕對時間的,而不是相對時間,所以它對系統時間的改變非常敏感。
知道了Timer的缺陷,接下來看ScheduledExecutorService
ScheduledExecutorService任務計劃ScheduledExecutorService主要功能就是可以將定時任務與執行緒池功能結合使用。
ScheduledExecutorService是個介面它有一個實現類ScheduledThreadPoolExecutor
而ScheduledThreadPoolExecutor的父類是ThreadPoolExecutor。
ScheduledThreadPoolExecutor類方法:
有些方法是重寫了父類的方法。如:submit,shutdown等方法。
透過Callable來執行延遲任務
示例:
public class ScheduledExecutorServiceDemo { public static void main(String[] args) throws Exception { ScheduledExecutorService sch = new ScheduledThreadPoolExecutor(5) ; ScheduledFuture<String> f1 = sch.schedule(() -> { System.out.println(Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; return "Task1" ; }, 5, TimeUnit.SECONDS) ; ScheduledFuture<String> f2 = sch.schedule(() -> { System.out.println(Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; return "Task2" ; }, 6, TimeUnit.SECONDS) ; System.out.println(f1.get()) ; System.out.println(f2.get()) ; } }
執行結果:
根據執行結果發現兩個任務是同時執行的,第二個任務是又過了1秒就執行了。也就是多個任務同時執行時他們的起始時間都是一樣的並不是一個執行完後另外一個要從0開始再計時。
透過Runnable來執行延遲任務
示例:
public class ScheduledExecutorServiceDemo2 { public static void main(String[] args) throws Exception { ScheduledExecutorService sch = new ScheduledThreadPoolExecutor(5) ; sch.schedule(() -> { System.out.println("A起始時間: " + Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; try { TimeUnit.SECONDS.sleep(3) ; } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("A結束時間: " + Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; }, 2, TimeUnit.SECONDS) ; sch.schedule(() -> { System.out.println("B起始時間: " + Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; try { TimeUnit.SECONDS.sleep(5) ; } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("B結束時間: " + Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; }, 2, TimeUnit.SECONDS) ; } }
執行結果:
執行週期性的任務scheduleAtFixedRate
示例(任務執行時間 > 週期時間):
public class ScheduledExecutorServiceDemo3 { public static void main(String[] args) throws Exception { ScheduledExecutorService sch = new ScheduledThreadPoolExecutor(5) ; sch.scheduleAtFixedRate(() -> { System.out.println("A起始時間: " + Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; try { TimeUnit.SECONDS.sleep(3) ; } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("A結束時間: " + Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; }, 1, 2, TimeUnit.SECONDS) ; } }
執行結果:
使用scheduleWithFixedDelay方法實現週期性任務執行
示例:
public class ScheduledExecutorServiceDemo4 { public static void main(String[] args) throws Exception { ScheduledExecutorService sch = new ScheduledThreadPoolExecutor(5) ; sch.scheduleWithFixedDelay(() -> { System.out.println("A起始時間: " + Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; try { TimeUnit.SECONDS.sleep(1) ; } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("A結束時間: " + Thread.currentThread().getName() + " 當前時間: "+ System.currentTimeMillis()) ; }, 1, 2, TimeUnit.SECONDS) ; } }
執行結果:
scheduleWithFixedDelay方法的作用是使得每次任務執行間隔為固定時間也就是scheduleWithFixedDelay方法的倒數第二個引數
完畢!!!