Java 延迟队列使用,大厂 HR 如何面试


写在最前面,我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家。扫码加微信好友进【程序员面试学习交流群】,免费领取。也欢迎各位一起在群里探讨技术。

 

延时队列,第一他是个队列,所以具有对列功能第二就是延时,这就是延时对列,功能也就是将任务放在该延时对列中,只有到了延时时刻才能从该延时对列中获取任务否则获取不到……

 

应用场景比较多,比如延时1分钟发短信,延时1分钟再次执行等,下面先看看延时队列demo之后再看延时队列在项目中的使用:

简单的延时队列要有三部分:第一实现了Delayed接口的消息体、第二消费消息的消费者、第三存放消息的延时队列,那下面就来看看延时队列demo。

一、消息体



 

[java] view plain copy


 



 




  1.  
  2. package com.delqueue;


     
  3.  


     
  4. import java.util.concurrent.Delayed;


     
  5. import java.util.concurrent.TimeUnit;


     
  6.  


     
  7. /**


     
  8. * 消息体定义 实现Delayed接口就是实现两个方法即compareTo 和 getDelay最重要的就是getDelay方法,这个方法用来判断是否到期……


     
  9. *


     
  10. * @author whd


     
  11. * @date 2017年9月24日 下午8:57:14


     
  12. */


     
  13. public class Message implements Delayed {


     
  14. private int id;


     
  15. private String body; // 消息内容


     
  16. private long excuteTime;// 延迟时长,这个是必须的属性因为要按照这个判断延时时长。


     
  17.  


     
  18. public int getId() {


     
  19. return id;


     
  20. }


     
  21.  


     
  22. public String getBody() {


     
  23. return body;


     
  24. }


     
  25.  


     
  26. public long getExcuteTime() {


     
  27. return excuteTime;


     
  28. }


     
  29.  


     
  30. public Message(int id, String body, long delayTime) {


     
  31. this.id = id;


     
  32. this.body = body;


     
  33. this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();


     
  34. }


     
  35.  


     
  36. // 自定义实现比较方法返回 1 0 -1三个参数


     
  37. @Override


     
  38. public int compareTo(Delayed delayed) {


     
  39. Message msg = (Message) delayed;


     
  40. return Integer.valueOf(this.id) > Integer.valueOf(msg.id) ? 1


     
  41. : (Integer.valueOf(this.id) < Integer.valueOf(msg.id) ? -1 : 0);


     
  42. }


     
  43.  


     
  44. // 延迟任务是否到时就是按照这个方法判断如果返回的是负数则说明到期否则还没到期


     
  45. @Override


     
  46. public long getDelay(TimeUnit unit) {


     
  47. return unit.convert(this.excuteTime - System.nanoTime(), TimeUnit.NANOSECONDS);


     
  48. }


     
  49. }


     


二、消息消费者



 

[java] view plain copy


 



 




  1.  
  2. package com.delqueue;


     
  3.  


     
  4. import java.util.concurrent.DelayQueue;


     
  5.  


     
  6. public class Consumer implements Runnable {


     
  7. // 延时队列 ,消费者从其中获取消息进行消费


     
  8. private DelayQueue<Message> queue;


     
  9.  


     
  10. public Consumer(DelayQueue<Message> queue) {


     
  11. this.queue = queue;


     
  12. }


     
  13.  


     
  14. @Override


     
  15. public void run() {


     
  16. while (true) {


     
  17. try {


     
  18. Message take = queue.take();


     
  19. System.out.println("消费消息id:" + take.getId() + " 消息体:" + take.getBody());


     
  20. } catch (InterruptedException e) {


     
  21. e.printStackTrace();


     
  22. }


     
  23. }


     
  24. }


     
  25. }


     




三、延时队列



 

[java] view plain copy


 



 




  1.  
  2. package com.delqueue;


     
  3.  


     
  4. import java.util.concurrent.DelayQueue;


     
  5. import java.util.concurrent.ExecutorService;


     
  6. import java.util.concurrent.Executors;


     
  7.  


     
  8. public class DelayQueueTest {


     
  9. public static void main(String[] args) {


     
  10. // 创建延时队列


     
  11. DelayQueue<Message> queue = new DelayQueue<Message>();


     
  12. // 添加延时消息,m1 延时3s


     
  13. Message m1 = new Message(1, "world", 3000);


     
  14. // 添加延时消息,m2 延时10s


     
  15. Message m2 = new Message(2, "hello", 10000);


     
  16. //将延时消息放到延时队列中


     
  17. queue.offer(m2);


     
  18. queue.offer(m1);


     
  19. // 启动消费线程 消费添加到延时队列中的消息,前提是任务到了延期时间


     
  20. ExecutorService exec = Executors.newFixedThreadPool(1);


     
  21. exec.execute(new Consumer(queue));


     
  22. exec.shutdown();


     
  23. }


     
  24. }


     




将消息体放入延迟队列中,在启动消费者线程去消费延迟队列中的消息,如果延迟队列中的消息到了延迟时间则可以从中取出消息否则无法取出消息也就无法消费。

这就是延迟队列demo,下面我们来说说在真实环境下的使用。

使用场景描述:

在打车软件中对订单进行派单的流程,当有订单的时候给该订单筛选司机,然后给当订单绑定司机,但是有时运气没那么好,订单进来后第一次没有筛选到合适的司机,但我们也不能就此结束派单,而是将该订单的信息放到延时队列中过个2秒钟在进行一次,其实这个2秒钟就是一个延迟,所以这里我们就可以使用延时队列来实现……

下面看看简单的流程图:

下面来看看具体代码实现:

在项目中有如下几个类:第一 、任务类 第二、按照任务类组装的消息体类 第三、延迟队列管理类

任务类即执行筛选司机、绑单、push消息的任务类



 

[java] view plain copy


 



 




  1.  
  2. package com.test.delayqueue;


     
  3. /**


     
  4. * 具体执行相关业务的业务类


     
  5. * @author whd


     
  6. * @date 2017年9月25日 上午12:49:32


     
  7. */


     
  8. public class DelayOrderWorker implements Runnable {


     
  9.  


     
  10. @Override


     
  11. public void run() {


     
  12. // TODO Auto-generated method stub


     
  13. //相关业务逻辑处理


     
  14. System.out.println(Thread.currentThread().getName()+" do something ……");


     
  15. }


     
  16. }


     

消息体类,在延时队列中这个实现了Delayed接口的消息类是比不可少的,实现接口时有一个getDelay(TimeUnit unit)方法,这个方法就是判断是否到期的

这里定义的是一个泛型类,所以可以将我们上面的任务类作为其中的task,这样就将任务类分装成了一个消息体



 

[java] view plain copy


 



 




  1.  
  2. package com.test.delayqueue;


     
  3.  


     
  4. import java.util.concurrent.Delayed;


     
  5. import java.util.concurrent.TimeUnit;


     
  6.  


     
  7. /**


     
  8. * 延时队列中的消息体将任务封装为消息体


     
  9. *


     
  10. * @author whd


     
  11. * @date 2017年9月25日 上午12:48:30


     
  12. * @param <T>


     
  13. */


     
  14. public class DelayOrderTask<T extends Runnable> implements Delayed {


     
  15. private final long time;


     
  16. private final T task; // 任务类,也就是之前定义的任务类


     
  17.  


     
  18. /**


     
  19. * @param timeout


     
  20. * 超时时间(秒)


     
  21. * @param task


     
  22. * 任务


     
  23. */


     
  24. public DelayOrderTask(long timeout, T task) {


     
  25. this.time = System.nanoTime() + timeout;


     
  26. this.task = task;


     
  27. }


     
  28.  


     
  29. @Override


     
  30. public int compareTo(Delayed o) {


     
  31. // TODO Auto-generated method stub


     
  32. DelayOrderTask other = (DelayOrderTask) o;


     
  33. long diff = time - other.time;


     
  34. if (diff > 0) {


     
  35. return 1;


     
  36. } else if (diff < 0) {


     
  37. return -1;


     
  38. } else {


     
  39. return 0;


     
  40. }


     
  41. }


     
  42.  


     
  43. @Override


     
  44. public long getDelay(TimeUnit unit) {


     
  45. // TODO Auto-generated method stub


     
  46. return unit.convert(this.time - System.nanoTime(), TimeUnit.NANOSECONDS);


     
  47. }


     
  48.  


     
  49. @Override


     
  50. public int hashCode() {


     
  51. return task.hashCode();


     
  52. }


     
  53.  


     
  54. public T getTask() {


     
  55. return task;


     
  56. }


     
  57. }


     

延时队列管理类,这个类主要就是将任务类封装成消息并并添加到延时队列中,以及轮询延时队列从中取出到时的消息体,在获取任务类放到线程池中执行任务



 

[java] view plain copy


 



 




  1.  
  2. package com.test.delayqueue;


     
  3.  


     
  4. import java.util.Map;


     
  5. import java.util.concurrent.DelayQueue;


     
  6. import java.util.concurrent.ExecutorService;


     
  7. import java.util.concurrent.Executors;


     
  8. import java.util.concurrent.TimeUnit;


     
  9. import java.util.concurrent.atomic.AtomicLong;


     
  10.  


     
  11. /**


     
  12. * 延时队列管理类,用来添加任务、执行任务


     
  13. *


     
  14. * @author whd


     
  15. * @date 2017年9月25日 上午12:44:59


     
  16. */


     
  17. public class DelayOrderQueueManager {


     
  18. private final static int DEFAULT_THREAD_NUM = 5;


     
  19. private static int thread_num = DEFAULT_THREAD_NUM;


     
  20. // 固定大小线程池


     
  21. private ExecutorService executor;


     
  22. // 守护线程


     
  23. private Thread daemonThread;


     
  24. // 延时队列


     
  25. private DelayQueue<DelayOrderTask<?>> delayQueue;


     
  26. private static final AtomicLong atomic = new AtomicLong(0);


     
  27. private final long n = 1;


     
  28. private static DelayOrderQueueManager instance = new DelayOrderQueueManager();


     
  29.  


     
  30. private DelayOrderQueueManager() {


     
  31. executor = Executors.newFixedThreadPool(thread_num);


     
  32. delayQueue = new DelayQueue<>();


     
  33. init();


     
  34. }


     
  35.  


     
  36. public static DelayOrderQueueManager getInstance() {


     
  37. return instance;


     
  38. }


     
  39.  


     
  40. /**


     
  41. * 初始化


     
  42. */


     
  43. public void init() {


     
  44. daemonThread = new Thread(() -> {


     
  45. execute();


     
  46. });


     
  47. daemonThread.setName("DelayQueueMonitor");


     
  48. daemonThread.start();


     
  49. }


     
  50.  


     
  51. private void execute() {


     
  52. while (true) {


     
  53. Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();


     
  54. System.out.println("当前存活线程数量:" + map.size());


     
  55. int taskNum = delayQueue.size();


     
  56. System.out.println("当前延时任务数量:" + taskNum);


     
  57. try {


     
  58. // 从延时队列中获取任务


     
  59. DelayOrderTask<?> delayOrderTask = delayQueue.take();


     
  60. if (delayOrderTask != null) {


     
  61. Runnable task = delayOrderTask.getTask();


     
  62. if (null == task) {


     
  63. continue;


     
  64. }


     
  65. // 提交到线程池执行task


     
  66. executor.execute(task);


     
  67. }


     
  68. } catch (Exception e) {


     
  69. e.printStackTrace();


     
  70. }


     
  71. }


     
  72. }


     
  73.  


     
  74. /**


     
  75. * 添加任务


     
  76. *


     
  77. * @param task


     
  78. * @param time


     
  79. * 延时时间


     
  80. * @param unit


     
  81. * 时间单位


     
  82. */


     
  83. public void put(Runnable task, long time, TimeUnit unit) {


     
  84. // 获取延时时间


     
  85. long timeout = TimeUnit.NANOSECONDS.convert(time, unit);


     
  86. // 将任务封装成实现Delayed接口的消息体


     
  87. DelayOrderTask<?> delayOrder = new DelayOrderTask<>(timeout, task);


     
  88. // 将消息体放到延时队列中


     
  89. delayQueue.put(delayOrder);


     
  90. }


     
  91.  


     
  92. /**


     
  93. * 删除任务


     
  94. *


     
  95. * @param task


     
  96. * @return


     
  97. */


     
  98. public boolean removeTask(DelayOrderTask task) {


     
  99.  


     
  100. return delayQueue.remove(task);


     
  101. }


     
  102. }


     

测试类



 

[java] view plain copy


 



 




  1.  
  2. package com.delqueue;


     
  3.  


     
  4. import java.util.concurrent.TimeUnit;


     
  5.  


     
  6. import com.test.delayqueue.DelayOrderQueueManager;


     
  7. import com.test.delayqueue.DelayOrderWorker;


     
  8.  


     
  9. public class Test {


     
  10. public static void main(String[] args) {


     
  11. DelayOrderWorker work1 = new DelayOrderWorker();// 任务1


     
  12. DelayOrderWorker work2 = new DelayOrderWorker();// 任务2


     
  13. DelayOrderWorker work3 = new DelayOrderWorker();// 任务3


     
  14. // 延迟队列管理类,将任务转化消息体并将消息体放入延迟对列中等待执行


     
  15. DelayOrderQueueManager manager = DelayOrderQueueManager.getInstance();


     
  16. manager.put(work1, 3000, TimeUnit.MILLISECONDS);


     
  17. manager.put(work2, 6000, TimeUnit.MILLISECONDS);


     
  18. manager.put(work3, 9000, TimeUnit.MILLISECONDS);


     
  19. }


     
  20.  


     
  21. }


     




OK 这就是项目中的具体使用情况,当然具体内容被忽略,整体框架就是这样,还有这里使用java的延时队列但是这种方式是有问题的如果如果down机则会出现任务丢失,所以也可以考虑使用mq、redis来实现

 

 


转载:https://www.cnblogs.com/barrywxx/p/8525907.html

推荐内容:
一个两年Java的面试总结
Java面试题(二)
Java解析word,获取文档中图片位置
JAVA基础总结【面试】
Java面试题总结-Day4
Java开发面试题汇总整理
面试 10:玩转 Java 选择和插入排序,附冒泡最终源码
浅谈Java中的深克隆和浅克隆(阿里面试)
【Java】广州三本秋招经历
53道Java线程面试题

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值