開發過程當中,每每有一些延遲任務的需求,好比:數據庫
這種狀況咱們通常採用延遲任務來實現。服務器
延遲任務和定時任務什麼區別呢?運維
數據庫輪詢分佈式
能夠經過一個線程定時掃描數據庫,根據時間執行update或insert操做。 能夠採用quartz實現:性能
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.2</version> </dependency>
Demo:ui
public class MyJob implements Job { public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("要去數據庫掃描啦。。。"); } public static void main(String[] args) throws Exception { // 建立任務 JobDetail jobDetail = JobBuilder.newJob(MyJob.class) .withIdentity("job1", "group1").build(); // 建立觸發器 每3秒鐘執行一次 Trigger trigger = TriggerBuilder .newTrigger() .withIdentity("trigger1", "group3") .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(3).repeatForever()) .build(); Scheduler scheduler = new StdSchedulerFactory().getScheduler(); // 將任務及其觸發器放入調度器 scheduler.scheduleJob(jobDetail, trigger); // 調度器開始調度任務 scheduler.start(); } }
這樣程序會每隔3秒,輸出:要去數據庫掃描啦。。。this
優勢:.net
缺點:線程
JDK延遲隊列指針
採用JDK自帶的DelayQueue實現,是一個無界阻塞隊列,只有在延遲時間知足時纔在隊列獲取元素,流程以下:
producer->生產一個任務->放入delayedQueue->經過poll()/take()方法獲取一個任務->交給消費者
Demo:
public class OrderDelay implements Delayed { private String orderId; private long timeout; OrderDelay(String orderId, long timeout) { this.orderId = orderId; this.timeout = timeout + System.nanoTime(); } public int compareTo(Delayed other) { if (other == this) return 0; OrderDelay t = (OrderDelay) other; long d = (getDelay(TimeUnit.NANOSECONDS) - t .getDelay(TimeUnit.NANOSECONDS)); return (d == 0) ? 0 : ((d < 0) ? -1 : 1); } // 返回距離你自定義的超時時間還有多少 public long getDelay(TimeUnit unit) { return unit.convert(timeout - System.nanoTime(), TimeUnit.NANOSECONDS); } void print() { System.out.println(orderId+"編號的訂單要刪除啦。。。。"); } } public class DelayQueueDemo { public static void main(String[] args) { // TODO Auto-generated method stub List<String> list = new ArrayList<String>(); list.add("00000001"); list.add("00000002"); list.add("00000003"); list.add("00000004"); list.add("00000005"); DelayQueue<OrderDelay> queue = new DelayQueue<OrderDelay>(); long start = System.currentTimeMillis(); for(int i = 0;i<5;i++){ //延遲三秒取出 queue.put(new OrderDelay(list.get(i), TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS))); try { queue.take().print(); System.out.println("After " + (System.currentTimeMillis()-start) + " MilliSeconds"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
輸出:
00000001編號的訂單要刪除啦。。。。 After 3003 MilliSeconds 00000002編號的訂單要刪除啦。。。。 After 6006 MilliSeconds 00000003編號的訂單要刪除啦。。。。 After 9006 MilliSeconds 00000004編號的訂單要刪除啦。。。。 After 12008 MilliSeconds 00000005編號的訂單要刪除啦。。。。 After 15009 MilliSeconds
優缺點:
時間片輪訓
原理:
相似於時鐘順時針執行,每一次移動算一個tick。時間片主要有三個屬性:
好比現實中的時鐘就是:ticksPerWheel = 60,tickDurateion = 1,timeUnit = s。
若是當前時針在1上,下一個任務要4秒後執行,這這個任務的線程回調放在5上。 若是要在20秒後執行,因爲一圈有8個位置,若是20秒,指針須要轉兩圈的5上面。
實現: 利用Netty的HashedWheelTimer:
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.24.Final</version> </dependency>
Demo:
public class HashedWheelTimerTest { static class MyTimerTask implements TimerTask{ boolean flag; public MyTimerTask(boolean flag){ this.flag = flag; } public void run(Timeout timeout) throws Exception { // TODO Auto-generated method stub System.out.println("要去數據庫刪除訂單了。。。。"); this.flag =false; } } public static void main(String[] argv) { MyTimerTask timerTask = new MyTimerTask(true); Timer timer = new HashedWheelTimer(); timer.newTimeout(timerTask, 5, TimeUnit.SECONDS); int i = 1; while(timerTask.flag){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(i+"秒過去了"); i++; } } }
輸出:
1秒過去了 2秒過去了 3秒過去了 4秒過去了 5秒過去了 要去數據庫刪除訂單了。。。。 6秒過去了
優缺點:
消息隊列
能夠採用消息隊列簡單的實現延遲隊列,好比:
優缺點: