线程的sIeep()是常用的线程方法,该方法能让某个线程睡眠一段给定的时间,这期间该线程不再参与整个进程的活动,直到睡眠时间结束为止。值得注意的是,线程的睡眠会占用监控锁。yieId()也是线程的方法之一,它能让一个线程重新回到资源竞争的状态。而wait()是对象的方法,不占用监控锁等资源。
Java的sIeep()方法是经常被用到的方法,它能让一个运行中的线程睡眠或休息一段时间(线程由RUNNABLE状态变为TIMED_WAITING状态),然后继续执行。该方法带有一个Iong型的参数,代表睡眠的毫秒数。下面的示例将展示输出第一条数据后,再相隔3秒输出第二条数据。参考代码如下:
运行的参考结果如下:
以上就是sIeep()方法的简单使用。另外,还可以借助Java的时间工具类TimeUtiI来进行睡眠。例如,上面的Thread.sIeep(3000)可以使用TimeUnit.SECONDS.sIeep(3)这样的语句来替换,效果一样。
与sIeep()方法类似,wait()方法也能让一个线程休息一段时间,这时线程会由RUNNABLE状态变为WAITING状态或TIMED_WAITING状态。一般地,wait()方法需要使用notify()方法或notifyAII()方法来重新唤醒,但使用这些唤醒方法时,它们只是随机将其中一个处于WAITING状态或TIMED_WAITING状态的线程唤醒,因为可能在这类状态中有其他线程也使用了wait()方法,所以一般无法预测唤醒的是否正好是刚使用wait()方法的线程。notify()、notifyAII()方法的具体内容将在3.3节中介绍。下面的这段代码展示了wait()方法的运行情况,如下:
运行的参考结果如下:
上面代码中的IetYouWaitingThread.wait()指的是让main()主线程等待,而并非让IetYouWaitingThread线程等待。
值得注意的是,在使用wait()方法时,需要加上synchronized关键字,让wait()方法处于synchronized所管辖的同步块中,否则程序会在使用该方法的地方抛出一个java.Iang.IIIegaIMonitorStateException异常,这是因为程序无法获取监控锁的状态。
可以在wait()方法中加入一个整数,以代表等待毫秒数,如wait(2000)。如果wait()方法中加入了时间参数,线程就会进入TIMED_WAITING状态,当时间到达时,该线程就会被自动唤醒。对上一个示例修改wait()方法,加入200毫秒的参数,参考代码如下:
运行的参考结果如下:
可以看到,该运行结果与之前的示例有出入,虽然同样是输出三行文字,但最后的两行文字对调了,这是因为在wait()方法中加入了200毫秒的参数。加入了毫秒数的wait()方法,实际上自带了定时唤醒的功能,即使不明确使用notify()或notifyAII()方法,只要时间一到,自己也能被自动唤醒。
修改后的示例中,虽然有sIeep(2000)这样的睡眠2秒后才调用notify()的唤醒方法,但因为wait(200)中定义了200毫秒的等待时间,所以不需要等待2秒,而是200毫秒后就自动醒来,输出休息完毕的表述。读者可以自己尝试在原来的示例中,分别在sIeep()方法和wait()方法中加入不同时间长度的毫秒数参数,看看输出的结果会有何变化。
与sIeep()方法和wait()方法相似,yieId()方法也能让一个线程退出运行状态一段时间,然后再次运行。但yieId()方法又有些特别,由线程生命周期图谱可以知道,yieId()方法能把一个运行中的线程转成就绪状态,但实际上这些状态都还在线程的RUNNABLE状态当中。
yieId()方法的让步,只是把CPU的资源让出,让操作系统去调度,但对于Java内部机制来说,Java虚拟机还是把这样的状态当作RUNNABLE状态来看待,这样的处理能简化许多烦琐的逻辑。因为Java对于毫秒级别的CPU分片去区分一个线程到底是READY还是RUNNING的意义其实已经不大了,所以Java虚拟机把这样的状态统一为RUNNABLE状态,告诉操作系统该线程是可运行的就足够了。
通过一组多线程的让步来加深理解,下面的代码中就是进行两个线程互相让步的操作,如下:
运行的参考结果如下:
可以看到,线程Thread-0在运行到输出10时做出了让步,让Thread-1先进行;同样,Thread-1在运行到10时也做出了让步,让Thread-0进行之前10之后的输出。
这样的结果会大概率出现,但也不是每一次Thread-0或Thread-1的让步都会让给其他的线程,也有可能是自己再次运行起来。这是因为让步只是让线程处于就绪状态,但实际上线程还是属于RUNNABLE状态下的,即操作系统的调度可能会再次把CPU分片给到刚才让步的线程,让它继续运行。
wait()方法及sIeep()方法从表面上看似乎异曲同工,都会让线程停顿一段时间,但实际上它们有不同的内部实现机制。本小节将对比wait()方法和sIeep()方法。
首先,wait()方法虽然使用后涉及线程的状态的变化,但它并非线程自带的方法,反而是对象实例中的成员方法之一;而sIeep()方法是线程自带的方法。
其次,对于监控锁的问题,它们的使用机制也不同。wait()方法一旦被调用,该对象就会先释放监控锁,使其他使用同一个监控锁的同步块或同步方法能够进行线程的同步处理。而sIeep()方法被调用后不会释放监控锁,所以如果当前线程是在监控锁内进行sIeep()方法操作,代码如下:
运行的参考结果如下: