很多时候,我们需要定时任务实现一些诸如刷新,心跳,保活等功能。这些定时任务往往逻辑很简单,使用定时任务的框架(例如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)。

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

  1. java.util.logging.Logger使用详解 (转)

    http://lavasoft.blog.51cto.com/62575/184492/ ************************************************* java. ...

  2. java.util.ResourceBundle国际化用法详解

    java.util.ResourceBundle国际化用法详解 初识国际化和ResourceBundle 这个类主要用来解决国际化和本地化问题.国际化和本地化可不是两个概念,两者都是一起出现的.可以说 ...

  3. java.util.logging.Logger 使用详解

    概述: 第1部分 创建Logger对象 第2部分 日志级别 第3部分 Handler 第4部分 Formatter 第5部分 自定义 第6部分 Logger的层次关系 参考 第1部分 创建Logger ...

  4. java.util.concurrent.atomic 包详解

    Atomic包的作用: 方便程序员在多线程环境下,无锁的进行原子操作 Atomic包核心: Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作 关于CAS compar ...

  5. java.util.logging.Logger使用详解

    一.创建Logger对象   static Logger getLogger(String name)           为指定子系统查找或创建一个 logger. static Logger ge ...

  6. java.util.ConcurrentModificationException 异常问题详解

    环境:JDK 1.8.0_111 在Java开发过程中,使用iterator遍历集合的同时对集合进行修改就会出现java.util.ConcurrentModificationException异常, ...

  7. 2.java.util.logging.Logger使用详解

    一.java.util.logging.Logger简介 java.util.logging.Logger不是什么新鲜东西了,1.4就有了,可是因为log4j的存在,这个logger一直沉默着, 其实 ...

  8. java.util.concuttent Callable Future详解

    在传统的多线程实现方式中(继承Thread和实现Runnable)无法直接获取线程执行的返回结果,如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 从 ...

  9. Java定时器Timer使用方法详解

    感谢大佬:https://www.jb51.net/article/129808.htm 一.概念 定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程的方式进行处理,所以它和 ...

  10. Java定时器Timer的使用详解

     转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6374714.html 定时器在Web开发中使用得不是很多.这里主要列举一下使用定时器的步骤,方便日后使用时查 ...

随机推荐

  1. Linux系统安装 tftp服务 NFS服务

    安装tftp服务 安装 sudo apt-get install tftp-hpa tftpd-hpa 配置文件 # /etc/default/tftpd-hpa TFTP_USERNAME=&quo ...

  2. RabbitMq了解

    RibbitMQ MQ优势 MQ的三大主要作用: 应用解耦.异步提速.流量削锋 应用解耦 系统的耦合性越高,容错性就越低,可维护性就越低: 解耦: 如果其中一个系统服务宕机,那么系统的其他服务将也无法 ...

  3. 06#Web 实战:实现可滑动的标签页

    实现效果图 本随笔只是记录一下大概的实现思路,如果感兴趣的小伙伴可以通过代码和本随笔的说明去理解实现过程.我的 Gitee 和 GitHub 地址.注意哦:这个只是 PC 上的标签页,手机端的没用,因 ...

  4. PW4052 是一颗适用于单节锂电池的、具有恒压/恒流充电模式的充电管理 IC

    PW4052 是一颗适用于单节锂电池的.具有恒压/恒流充电模式的充电管理 IC.该芯片采用开关型的工作模式, 能够为单节锂电池提供快速. 高效且简单的充电管理解决方案.PW4052 采用三段式充电管理 ...

  5. forms组件源码剖析

    一:forms组件源码剖析 1.forms组件源码切入点: 1.0 form_obj.is_valid() 2.0 def is_valid(self): """ Ret ...

  6. before-after-hook钩子函数

    before-after-hook 最近看别人的代码,接触到一个插件,before-after-hook,百度搜一圈也没有看到什么地方有教程,看这个字面意思是一个hook,和axios里面的拦截器,v ...

  7. 一文聊透Apache Hudi的索引设计与应用

    Hudi索引在数据读和写的过程中都有应用.读的过程主要是查询引擎利用MetaDataTable使用索引进行Data Skipping以提高查找速度;写的过程主要应用在upsert写上,即利用索引查找该 ...

  8. week_1

    Andrew Ng机器学习笔记---by OrangeStar Week 1 A computer program is said to learn from experience E with re ...

  9. [OpenCV实战]38 基于OpenCV的相机标定

    文章目录 1 什么是相机标定? 2 图像形成几何学 2.1 设定 2.1.1 世界坐标系 2.1.2 相机坐标系 2.1.3 图像坐标系 2.2 图像形成方法总结 3 基于OpenCV的相机标定原理 ...

  10. P8618 [蓝桥杯 2014 国 B] Log 大侠

    简要题意 给你一个长度为 \(n\) 的正整数序列 \(a\),有 \(m\) 个询问,每一个询问给出一个区间 \([l,r]\).定义函数 \(f(x)=\lfloor\log_{2}(x)+1\r ...