当前位置: 首页 > news >正文

定时器(java)

文章目录

    • 什么是定时器
    • 标准库中的定时器
    • 实现定时器

什么是定时器

定时器是一种编程工具,用于特定时间或固定时间间隔执行任务(代码逻辑),其核心功能是管理任务的调度和执行

标准库中的定时器

  1. Timer 和 TimerTask 类
    定位:早期的单线程定时器实现,适用于简单场景。
    核心类:
    Timer:用于调度任务。
    TimerTask:抽象类,需继承并实现 run() 定义任务逻辑。

实现定时器

import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;

class Mytask implements Comparable<Mytask>{
    private Runnable runnable;
    // 定义任务执行时间
    private long time;

    public Mytask(Runnable runnable,long delay){
        if(runnable==null){
            try{
                throw new IllegalAccessException("任务不能为空");
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        if(delay<0){
            try {
                throw new IllegalAccessException("时间不能小于0");
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        this.runnable=runnable;
        //计算出任务的具体时间
        this.time=delay+System.currentTimeMillis();
    }

    public Runnable getRunnable() {
        return runnable;
    }

    public long getTime() {
        return time;
    }

    @Override
    public int compareTo(Mytask o) {
       if(this.getTime()-o.getTime()>0)return 1;
       else if(this.getTime()-o.getTime()==0)return 0;
       else return -1;
    }
}

public class MyTimer {
    //定义线程内容
   private PriorityBlockingQueue<Mytask> priorityBlockingQueue=new PriorityBlockingQueue<>();

   public MyTimer() {
       //扫描线程
       Thread th1 = new Thread (()-> {

               while (true) {
                   try {
                       Mytask take = priorityBlockingQueue.take();
                       //判断有没有到时间
                       long times = System.currentTimeMillis();
                       if (times >= take.getTime()) {
                           //时间到了执行任务
                           take.getRunnable().run();
                       } else {
                           long waittime = take.getTime() - times;
                           //没有到时间放回阻塞队列
                           priorityBlockingQueue.put(take);
                           synchronized (this) {
                               //进行休眠
                               this.wait(waittime);
                           }
                       }
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
       });
       th1.start();
       //创建一个后台线程
       Thread thread = new Thread (()->{
           while (true) {
               synchronized (this) {
                   notifyAll();
               }
               //休眠一会
               try {
                   TimeUnit.MICROSECONDS.sleep(100);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
       });
       //设置为后台线程
       thread.setDaemon(true);
       thread.start();
   }
   public void schedule(Runnable runnable,long time) {
       //构建一个Mytask对象
       Mytask mytask=new Mytask(runnable,time);
       //放入阻塞数组
       priorityBlockingQueue.put(mytask);
       //唤醒等待线程
       synchronized (this){
           this.notifyAll();
       }
    }
}

在这里插入图片描述
在这里插入图片描述
这里要注意当将锁在加在整个while(扫描线程)时会产生死锁

/扫描线程
       Thread th1 = new Thread (()-> {
           synchronized (this) {
               while (true) {
                   try {
                       Mytask take = priorityBlockingQueue.take();
                       //判断有没有到时间
                       long times = System.currentTimeMillis();
                       if (times >= take.getTime()) {
                           //时间到了执行任务
                           take.getRunnable().run();
                       } else {
                           long waittime = take.getTime() - times;
                           //没有到时间放回阻塞队列
                           priorityBlockingQueue.put(take);

                           //进行休眠
                           this.wait(waittime);
                       }
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
           }
       });

在这里插入图片描述
引起死锁的原因

锁的持有与阻塞调用:
扫描线程 th1 的整个循环被包裹在 synchronized(this) 块中。
当队列为空时,priorityBlockingQueue.take() 会阻塞,但此时 th1 仍持有 this 锁。
其他线程(如主线程调用 schedule)需要获取 this 锁才能添加任务,但无法获得锁,导致它们被阻塞。

互相等待的僵局:
th1 在等待队列中有任务(被 take() 阻塞),但其他线程无法添加任务(因为它们需要 this 锁)。
其他线程在等待 th1 释放锁,而 th1 在等待其他线程添加任务,形成死锁。

1.synchronized和wait的使用:
在MyTimer类的构造方法中,扫描线程t1并使synchronized(this)进行同步,台线程 (thread) 也使用 synchronized (this) 来调用 notifyAll()。

2.wait 和 notifyAll 的逻辑
在从队列中拿到线程任务时如果线程任务还没有到会调用wait()进行等待,
在主线程在这里插入图片描述
后台线程 (thread) 也在尝试调用 notifyAll(),但它是在 synchronized (this) 块中执行的。如果此时没有其他线程持有 MyTimer 对象的锁,后台线程将无法执行 notifyAll()。所以锁得不到释放
任务的执行时间:

3.如果 Mytask 的执行时间设置得非常短(例如 0 毫秒),可能会导致 th1 线程不断地将任务放回队列并调用 wait(),而后台线程可能无法及时调用 notifyAll(),从而导致 th1 永远处于等待状态。

相关文章:

  • Linux安装MySQL数据库并使用C语言进行数据库开发
  • 【从零实现Json-Rpc框架】- 项目设计篇
  • C语言指针(二)
  • 有哪一些解放生产力的AI工具【前端】
  • GAMES101-现代计算机图形学入门(Assignment5)
  • 练习:自动驾驶
  • Linux中的基本开发工具(上)
  • 系统与网络安全------网络应用基础(3)
  • 图解CNN、RNN、LSTM
  • 【杂谈】-人工智能驱动的编码:提升效率还是增加网络安全隐患?
  • c++ primer 阅读手记 第三章
  • js中async+await+promise的用法及常见问题总结
  • Linux多线程详解
  • Docker镜像相关命令(Day2)
  • 【大模型】数字人 EchoMimicV2 的环境配置和使用
  • 基于Logisim的汉字显示模拟实验
  • 详细比较StringRedisTemplate和RedisTemplate的区别及使用方法,及解决融合使用方法
  • OLLVM 增加 CC++ 字符串加密功能
  • std::unordered_map和 std::map的区别
  • 代码随想录算法训练营第十一天| 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素
  • 网站运营推广怎么做/竞价托管代运营公司
  • 邯郸seo/seo入门基础知识
  • 局域网站建设模版/制作网站需要什么软件
  • 信息门户网站制作/百度秒收录技术
  • 仿券妈妈网站开发/seo指的是什么意思
  • 自己做网站推广/汕头网站建设开发