Java 多线程系列 5:sleep()
前面文章讲线程的一生时,讲到线程也有自己的生老病死。其实线程也可以“睡觉”,这里说的是可以,而不是必须。Java 的线程类 Thread 提供了一个 sleep(millis)方法,其作用是让当前执行的线程挂起来一段时间,让出 CPU 控制权,让别的线程有机会抢占 CPU 来运行。
使用场景
那什么场景下需要用到 sleep 呢?
第一种场景就是:轮询。比如我们需要调用一个需要比较长时间才能执行完成的服务,一般采用异步调用的方式,客户端调用完服务后就直接返回了,但是调用客户端会另外起一个线程每隔一段时间去轮询后端是否完成。
在上述代码中,我们模拟了一个耗时的后端服务,每隔 10 毫秒去轮询一次返回的结果。如果我们不调用 sleep,那么这个线程将一致占用 CPU 不释放。
还有一种不那么常见的场景,就是用 sleep 方法来模拟定时调度。比如我们每天早上 6 点要发送一封邮件,那么我们可以用一个独立的线程来实现。具体做法也很简单,每次线程计算一次当前时间点离下个早上 6 点之间有多少毫秒,假设为 m,那么调用 Thread.sleep(m),等发送邮件后,继续 sleep。当然如果你要大批量的去发送邮件,我们不建议使用 sleep 来做,有更优化的方式。
有一种常见的错误使用场景,就是使用 sleep 来进行线程协同。比如 A 线程要依赖 B 线程的输出,然后 A 线程预计 1 小时候 B 线程应该可以完成,然后 sleep 1 个小时后恢复执行,或者每隔 10 分钟轮询一次。这种做法是极其不健壮的,因为没有人能保证 B 线程什么时候能执行完。线程之间要协同,应该用我们之前文章介绍的 wait/nofityAll 来实现。
sleep 和 wait 的区别
sleep 和 wait 都会让当前执行线程进入阻塞状态,但它们有着本质的区别。
第一个区别:sleep 是线程主动的释放 CPU 控制权,而 wait 是线程发现所需要的资源无法满足,因此被动的陷入等待。一个主动触发,一个被动触发。
第二个区别:sleep 和同步无关,可以在任何地方用。而 wait 必须在 synchronized 中使用。
第三个区别:sleep 不会释放锁,但是 wait 会释放锁。因此在临界代码中要慎用 sleep 方法。
Interuppted 异常处理
进入 sleep 的程序会被中断,就好像我们睡觉时被闹钟吵醒一样。另外一个线程可以调用某个线程的 interrupt()方法来中断其睡眠状态。
捕获 InterruptedException 后,线程可以在退出前做一些清理善后的工作,比如释放数据库连接等。这里就不具体展开了。
一个有趣的问题
如果调用 Thread.sleep(0),那会发生什么?线程会 sleep 吗?CPU 会被中断吗?
sleep(0)的效果是,触发操作系统立即进行一次 CPU 的竞争,竞争的结果有可能是当前线程继续获得 CPU 控制权,也有可能被其它线程抢占。这种行为用大白话来描述就是:我主动让出 CPU 了,别说我吃独食,你有机会可以抢占,但我不会睡觉,我们一起抢,嘿嘿。
版权声明: 本文为 InfoQ 作者【BigBang!】的原创文章。
原文链接:【http://xie.infoq.cn/article/e5a85163d738465ab458103a7】。文章转载请联系作者。
评论