添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Thread.sleep的副作用

很多java程序员喜欢用Thread.sleep方法来让线程睡眠,来实现定时定时轮询效果。

while (true) {
      if( check() ) {
          //执行某个操作
      Thread.sleep(10);
  }

这么做可以让线程每个10毫秒陷入一次睡眠,避免while死循环大量暂用CPU时间。然而Thread.sleep的执行并非没有成本,如果循环中sleep的时间过短,开销也非常大。

sleep的主要开销来自线程的切换,当代码运行到Thread.sleep时,当前线程进入time_wait状态,通常我们会说线程进入睡眠状态,此时该线程不需要占用CPU了,操作系统就会执行一次线程切换,将该线程的CPU时间给其他线程使用,这种切换叫做主动线程切换,是该线程使用sleep主动让出CPU时间,而不是CPU时间用完了操作系统将CPU使用权强行切换给其他线程。

在linux操作系统中有CPU上下文切换的数据统计

执行命令 vmstat 1,就可以看到整个操作系统每1秒CPU上下文切换的统计

其中cs列就是CPU上下文切换的统计。当然,CPU上下文切换不等价于线程切换,很多操作会造成CPU上下文切换

  • 线程、进程切换
  • 系统调用
  • 中断

都会造成CPU上下文切换

如果想看某一个线程\进程的上下文切换情况,有2中方法

使用pidstat命令

执行命令 pidstat -w -p 5598 1 ,显示进程5598每一秒的相称切换情况

其中cswch表示主动切换,nvcswch表示被动切换。从统计数据中看到,该进程每秒主动切换次数达到将近500次,因此代码中存在大量的 睡眠\唤醒 操作。

从进程的状态信息中查看

通过命令 cat /proc/5598/status 查看进程的状态信息

voluntary_ctxt_switches: 40469351
nonvoluntary_ctxt_switches: 2268

结果中这2项就是该进程从启动到当前总的上下文卡换情况。

网上有人对线程上下文切换时间做过计算,每一次大概需要消耗5微妙左右CPU时间。而每一次sleep会造成2次线程切换,一次切出去, 一次切回来,那么就会消耗10微妙CPU时间。这个消耗虽然很小,但是架不住次数多,就像上面的示例进程,每条切换将近500次数,那么这个进程每运行1秒,就会平白浪费500微妙也就是5毫秒在线程切换上,这是一种可耻的浪费。

与此同时,sleep除了会造成线程切换之外,它还是一个系统调用。在linux下用 strace命令跟踪发现,sleep最终执行的操作系统系统调用是 futex

通过命令

strace -ff -t -o log/xx.log java -jar xx.jar

跟踪发现大量如下系统调用日志

17:29:13 futex(0x7f48bc009354, FUTEX_WAIT_BITSET_PRIVATE, 1, {tv_sec=7710830, tv_nsec=110671740}, 0xffffffff) = -1 ETIMEDOUT (Connection timed out)
17:29:13 futex(0x7f48bc009328, FUTEX_WAKE_PRIVATE, 1) = 0
17:29:13 futex(0x7f48bc009354, FUTEX_WAIT_BITSET_PRIVATE, 1, {tv_sec=7710830, tv_nsec=120923628}, 0xffffffff) = -1 ETIMEDOUT (Connection timed out)
17:29:13 futex(0x7f48bc009328, FUTEX_WAKE_PRIVATE, 1) = 0
17:29:13 futex(0x7f48bc009354, FUTEX_WAIT_BITSET_PRIVATE, 1, {tv_sec=7710830, tv_nsec=131149338}, 0xffffffff) = -1 ETIMEDOUT (Connection timed out)
17:29:13 futex(0x7f48bc009328, FUTEX_WAKE_PRIVATE, 1) = 0
17:29:13 futex(0x7f48bc009354, FUTEX_WAIT_BITSET_PRIVATE, 1, {tv_sec=7710830, tv_nsec=141353193}, 0xffffffff) = -1 ETIMEDOUT (Connection timed out)
17:29:13 futex(0x7f48bc009328, FUTEX_WAKE_PRIVATE, 1) = 0
17:29:13 futex(0x7f48bc009354, FUTEX_WAIT_BITSET_PRIVATE, 1, {tv_sec=7710830, tv_nsec=151570907}, 0xffffffff) = -1 ETIMEDOUT (Connection timed out)
17:29:13 futex(0x7f48bc009328, FUTEX_WAKE_PRIVATE, 1) = 0