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

最新文章

  1. Word/Excel 在线预览
  2. vs中使用git
  3. C#中的多线程 - 基础知识
  4. idea 工程添加svn关联
  5. apache安装后编译新模块
  6. 商务部公开微软持有的Android技术专利
  7. C语言带参数的main函数
  8. 【转】SharePoint 2013 stand alone服务器安装
  9. 【Visual C++】绘图函数BitBlt的使用方法
  10. Eratosthenes,筛法求素数
  11. jdk源码阅读笔记-Integer
  12. MySQL事物管理
  13. SpringBoot整合ssm
  14. java中线程池的使用
  15. MySQL死锁分析
  16. Python之路----迭代器与生成器
  17. jQuery学习-显示与隐藏
  18. ArcEngine交互画线
  19. Centos7yum安装LNMP
  20. mysql中int(M) tinyint(M)中M的作用

热门文章

  1. 数据结构初阶--顺序表(讲解+C++类模板实现)
  2. CAP 7.0 版本发布通告 - 支持延迟消息,性能炸了?
  3. Docker定时删除none镜像
  4. 跟我学Python图像处理丨图像分类原理与案例
  5. Velero 系列文章(一):基础
  6. 分布式计算MapReduce究竟是怎么一回事?
  7. 为什么总是应该考虑给定 List 的初始大小
  8. vue实现移动端左右菜单双向联动效果
  9. 痞子衡嵌入式:Farewell, 我的写博故事2016-2019
  10. centos更新glibc-2.28