开发过程当中,每每有一些延迟任务的需求,好比:数据库
这种状况咱们通常采用延迟任务来实现。服务器
延迟任务和定时任务什么区别呢?运维
数据库轮询分布式
能够经过一个线程定时扫描数据库,根据时间执行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秒过去了
优缺点:
消息队列
能够采用消息队列简单的实现延迟队列,好比:
优缺点: