当前位置:   article > 正文

JavaEE——多线程定时器(Timer)_java timer指定线程数

java timer指定线程数

 定时器是什么?定时器能做什么?本篇文章带你深入了解并实现


目录

一、定时器

 1.1java标准库定时器

1.2定时器代码

二、模拟定时器实现

2.1先创建个任务类,表示执行的任务是啥,任务啥时候执行?

2.2创建定时器类

三、完整代码及注意事项


一、定时器

定时器是什么?

定时器也是软件开发中的一个重要组件. 类似于一个 "闹钟". 达到一个设定的时间之后, 就执行某个指定好的代码
定时器是一种实际开发中非常常用的组件,比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连,比如一个 Map, 希望里面的某个 key 在 3s 之后过期,类似于这样的场景就需要用到定时器

 1.1java标准库定时器

标准库提供的定时器:Timer在java.util这个集合类

 

1.2定时器代码

标准库中提供了一个 Timer 类,Timer 类的核心方法为 schedule
schedule 包含两个参数,第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 (单位为毫秒)

安排一个工作,这个工作不是立即完成的,而是未来某个时间点~~

  1. public class ThreadDemo6 {
  2. public static void main(String[] args) {
  3. Timer timer = new Timer();
  4. timer.schedule(new TimerTask() {
  5. @Override
  6. public void run() {
  7. System.out.println("hello");
  8. }
  9. },4000);
  10. timer.schedule(new TimerTask() {
  11. @Override
  12. public void run() {
  13. System.out.println("hello");
  14. }
  15. },3000);
  16. timer.schedule(new TimerTask() {
  17. @Override
  18. public void run() {
  19. System.out.println("hello");
  20. }
  21. },2000);
  22. timer.schedule(new TimerTask() {
  23. @Override
  24. public void run() {
  25. System.out.println("hello");
  26. }
  27. },1000);
  28. }
  29. }

  

 可见线程还没有结束,那这是因为什么呢?

是因为Timer里面内置了线程,(还是前台线程)会阻止线程结束

二、模拟定时器实现

定时器,内部管理的不仅仅是一个任务,可以管理很多任务的!!

虽然任务有很多,但是他们的触发时间是不同的,每次都找到这些任务中,最先到达的任务执行;一个线程先执行最早的任务,做完了之后再执行第二早的,那么应该用什么去存储这些任务呢?

当然是堆!!!java标准库中提供了带优先级的阻塞队列

  • 队列中的每个元素是一个 Task 对象
  • Task 中带有一个时间属性, 队首元素就是即将要执行的任务
  • 同时有一个 worker 线程一直扫描队首元素, 看队首元素是否需要执行

2.1先创建个任务类,表示执行的任务是啥,任务啥时候执行?

  1. // 表示一个任务.
  2. class MyTask implements Comparable<MyTask>{
  3. public Runnable runnable;
  4. // 为了方便后续判定, 使用绝对的时间戳.
  5. public long time;
  6. public MyTask(Runnable runnable, long delay) {
  7. this.runnable = runnable;
  8. // 取当前时刻的时间戳 + delay, 作为该任务实际执行的时间戳
  9. this.time = System.currentTimeMillis() + delay;
  10. }
  11. @Override
  12. public int compareTo(MyTask o) {
  13. return (int) (this.time - o.time);
  14. }
  15. }

2.2创建定时器类

  1. class MyTimer{
  2. // 这个结构, 带有优先级的阻塞队列. 核心数据结构
  3. private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
  4. // 创建一个锁对象
  5. private Object locker = new Object();
  6. // 此处的 delay 是一个形如 3000 这样的数字 (多长时间之后, 执行该任务)
  7. public void schedule(Runnable runnable,long delay){
  8. // 根据参数, 构造 MyTask, 插入队列即可.
  9. MyTask myTask = new MyTask(runnable,delay);
  10. queue.put(myTask);
  11. synchronized (locker){
  12. locker.notify();
  13. }
  14. }
  15. // 在这里构造线程, 负责执行具体任务了.
  16. public MyTimer(){
  17. Thread t = new Thread(() -> {
  18. while (true){
  19. try {
  20. // 阻塞队列, 只有阻塞的入队列和阻塞的出队列, 没有阻塞的查看队首元素.
  21. synchronized (locker) {
  22. MyTask myTask = queue.take();
  23. long curTime = System.currentTimeMillis();
  24. if (myTask.time <= curTime) {
  25. // 时间到了, 可以执行任务了
  26. myTask.runnable.run();
  27. } else {
  28. // 时间还没到
  29. // 把刚才取出的任务, 重新塞回队列中.
  30. queue.put(myTask);
  31. locker.wait(myTask.time - curTime);
  32. }
  33. }
  34. } catch (InterruptedException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. });
  39. t.start();
  40. }
  41. }

注意几个点:

1.要让任务类去实现Comparable接口,以至于可以放进PriorityBlockingQueue

2.使用wait去避免忙等,浪费系统资源

3.在放任务的时候,用notify来唤醒线程

三、完整代码及注意事项

  1. /**
  2. * @author xyk的电脑
  3. * @version 1.0
  4. * @description: TODO
  5. * @date 2023/3/25 16:10
  6. */
  7. public class ThreadDemo1 {
  8. public static void main(String[] args) {
  9. MyTimer myTimer = new MyTimer();
  10. myTimer.schedule(new Runnable() {
  11. @Override
  12. public void run() {
  13. System.out.println("hello4");
  14. }
  15. }, 4000);
  16. myTimer.schedule(new Runnable() {
  17. @Override
  18. public void run() {
  19. System.out.println("hello3");
  20. }
  21. }, 3000);
  22. myTimer.schedule(new Runnable() {
  23. @Override
  24. public void run() {
  25. System.out.println("hello2");
  26. }
  27. }, 2000);
  28. myTimer.schedule(new Runnable() {
  29. @Override
  30. public void run() {
  31. System.out.println("hello1");
  32. }
  33. }, 1000);
  34. System.out.println("hello");
  35. }
  36. }
  37. // 表示一个任务.
  38. class MyTask implements Comparable<MyTask>{
  39. public Runnable runnable;
  40. // 为了方便后续判定, 使用绝对的时间戳.
  41. public long time;
  42. public MyTask(Runnable runnable, long delay) {
  43. this.runnable = runnable;
  44. // 取当前时刻的时间戳 + delay, 作为该任务实际执行的时间戳
  45. this.time = System.currentTimeMillis() + delay;
  46. }
  47. @Override
  48. public int compareTo(MyTask o) {
  49. return (int) (this.time - o.time);
  50. }
  51. }
  52. class MyTimer{
  53. // 这个结构, 带有优先级的阻塞队列. 核心数据结构
  54. private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
  55. // 创建一个锁对象
  56. private Object locker = new Object();
  57. // 此处的 delay 是一个形如 3000 这样的数字 (多长时间之后, 执行该任务)
  58. public void schedule(Runnable runnable,long delay){
  59. // 根据参数, 构造 MyTask, 插入队列即可.
  60. MyTask myTask = new MyTask(runnable,delay);
  61. queue.put(myTask);
  62. synchronized (locker){
  63. locker.notify();
  64. }
  65. }
  66. // 在这里构造线程, 负责执行具体任务了.
  67. public MyTimer(){
  68. Thread t = new Thread(() -> {
  69. while (true){
  70. try {
  71. // 阻塞队列, 只有阻塞的入队列和阻塞的出队列, 没有阻塞的查看队首元素.
  72. synchronized (locker) {
  73. MyTask myTask = queue.take();
  74. long curTime = System.currentTimeMillis();
  75. if (myTask.time <= curTime) {
  76. // 时间到了, 可以执行任务了
  77. myTask.runnable.run();
  78. } else {
  79. // 时间还没到
  80. // 把刚才取出的任务, 重新塞回队列中.
  81. queue.put(myTask);
  82. locker.wait(myTask.time - curTime);
  83. }
  84. }
  85. } catch (InterruptedException e) {
  86. e.printStackTrace();
  87. }
  88. }
  89. });
  90. t.start();
  91. }
  92. }

注意事项:

1.使用wait来等待,而不是sleep,wait方便随时提前唤醒

2.wait的参数是“超时时间”,时间达到一定程度后,还没有notify就不等,如果时间还没到,就notify立即返回

3.如果将锁加进内部:

会导致新进来的最早的任务“空打一炮”,导致新的任务无法及时执行了;关键要点:多线程的调度是随机的,无序的!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/495539
推荐阅读
相关标签
  

闽ICP备14008679号