首页 > 编程语言 >使用java.util.Timer实现定时任务,详解Thread.sleep() in a loop, probably busy-waiting问题

使用java.util.Timer实现定时任务,详解Thread.sleep() in a loop, probably busy-waiting问题

时间:2023-02-21 15:47:51浏览次数:41  
标签:busy java Thread 代码 Timer waiting sleep

很多时候,我们需要定时任务实现一些诸如刷新,心跳,保活等功能。这些定时任务往往逻辑很简单,使用定时任务的框架(例如springboot @Scheduled)往往大材小用。

下面是一个定时任务的典型写法,每隔30s发送心跳

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    //发送心跳的业务代码
                    heartbeat();
                    Thread.sleep(1000L * 30);
                } catch (Exception e) {
                    //print the error log
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }

如果你使用了IDEA或者其他的Java集成开发环境,你会发现编辑器会提示你Call to 'Thread.sleep()' in a loop, probably busy-waiting 点开提示信息,发现这样的写法有可能会导致忙等待死锁

忙等待 busy-waiting占用大量cpu资源,cpu利用率会达到99%,可能会完全吃掉一核cpu资源,导致其他业务甚至是宿主机的异常。

你可能会说,这样的写法怎么会导致忙等待 busy-waiting呢,我明明已经sleep()了呀,心跳任务每隔30s才执行一次啊。

如果heartbeat()抛出了异常(空指针,代码错误,网络错误等),sleep()语句就会跳过,进入了异常分支,休眠30s的目的无法达到,程序就会进入死循环,以疯狂的速度执行heartbeat()语句。更有甚者,如果你捕获异常并打印日志,日志甚至能很快写满整个硬盘。

当然,你也可以将sleep语句提到前面来,先执行Thread.sleep(),这样,可以规避忙等待风险。但是还有另外一个坑在等待着你。点开Thread.sleep()文档

Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors.

重点在这一句The thread does not lose ownership of any monitors. monitor就是Java的重量级锁,平时我们使用的synchronized关键字就是基于monitor实现的。也就是说,线程在sleep的过程中并不会释放所持有的锁,这会导致严重的并发问题,甚至是死锁。你可能又会说,我写的代码里没有锁没有synchronized关键字,我能不能放心使用呢?

答案是不能,你的代码里没锁,不代表你依赖的代码里没锁,不代表后续的维护者不会加锁。这是一个技术债务,在绝大多数的情况下都不会出问题,但也许有一天会暴雷。

作为一个负责人的开发者,作为一个有着代码洁癖的人,作为一个无法忍受IDEA黄色提示的人,作为一个简洁至上的人,作为一个不想滥用框架的人,我推荐使用jdk自带的java.util.Timer代码如下

    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    //发送心跳的业务代码
                    heartbeat();
                } catch (Exception e) {
                    //print the error log
                    e.printStackTrace();
                }

            }
        }, 1000L * 30, 1000L * 30);
    }

同样实现的是30s间隔执行心跳操作,使用Timer不会有上述我们说的忙等待死锁的风险。Timer内部使用了一个线程,和我们单独new Thread()的效果是一样的。值得一提的是,Timer是基于wait(),notify()机制实现的,与sleep()相比,wait()会释放锁(也就是monitor)。

标签:busy,java,Thread,代码,Timer,waiting,sleep
From: https://www.cnblogs.com/bianheng/p/17140219.html

相关文章

  • java只有值传递
    Java中将实参传递给方法(或函数)的方式是值传递:如果参数是基本类型的话,很简单,传递的就是基本类型的字面量值的拷贝,会创建副本。如果参数是引用类型,传递的就是实参所引......
  • 来一波骚操作,Java内存模型
    文章整理自博学谷狂野架构师什么是JMM并发编程领域的关键问题线程之间的通信线程的通信是指线程之间以何种机制来交换信息。在编程中,线程之间的通信机制有两种,共享......
  • 苹果内购 java后端验证订单(转载)
    文章转载自:  https://www.jianshu.com/p/05699ff6f042看前须知往下看之前先说清楚ApplePay和苹果内购不是一回事;ApplePay:是类似与支付宝、微信等支付等,用于购买......
  • javax.servlet.ServletContext.getVirtualServerName()Ljava/lang/String; spring
    2023-02-2111:44:13.924ERROR27256---[main]o.s.b.d.LoggingFailureAnalysisReporter:***************************APPLICATIONFAILEDTOSTART************......
  • idea新建spring boot 项目右键无package及java类的选项
    新创建的spring boot项目,只有一个默认的资源目录及启动配置。在group 的目录下右键新建包路径时 ,发现没有package选项,也没有JavaClass的选项:  解决办法:F......
  • java web项目在linux部署、启动,查看系统配置常用的linux命令总结
    本文为博主原创,未经允许不得转载:      以下为在工作中常用的linux命令进行了总结,主要在项目安装及启动,及编辑部署文件时应用较多1.gz文件是一种压缩文件。以·ta......
  • Java数组
    1.数组数组是指一组数据的集合,数组中每个数据称为元素或单元。声明数组包括数组以及数组名称,如神明一个int类型的一维数组。1:intarray[];//数组声明......
  • 数组的声明 语法强化 java 230221
    目标强化声明数组的格式//注意数组是类型名[]Stringa;//字符串变量String[]b;//字符串数组变量intm;int[]n;//数组定义等号右边new数据类型[容量]b=newSt......
  • 【JavaScript】21_对象的结构
    9、对象的结构对象中存储属性的区域实际有两个:对象自身直接通过对象所添加的属性,位于对象自身中在类中通过x=y的形式添加的属性,位于对象自身中原型对象(prototype)对象中......
  • 【JavaScript】22_原型对象与修改原型
    11、原型对象访问一个对象的原型对象对象.protoObject.getPrototypeOf(对象)原型对象中的数据:对象中的数据(属性、方法等)constructor(对象的构造函数)注意:原型对象也有原型......