只谈blocked、waiting和timed_waiting
大家好,我是欧阳方超,微信公众号同名。
1 概述
Java中线程的状态有六种,我们这里先讨论blocked、waiting和timed_waiting这三种,因为我感觉这三种不好理解。
2 三种状态
2.1 blocked
2.1.1 含义与触发条件
blocked(阻塞)是线程的状态之一。线程进入blocked状态通常是因为它在等待获取一个有synchronized关键字保护的对象的锁,此时该线程被暂时阻止执行期后续代码。简单说就是求锁而不得。
2.1.2 CPU 时间片占用情况
线程处于blocked状态时,它不会占用CPU时间片。因为CPU时间片是分配给处于Runnable状态的线程,这些线程能够在CPU上执行指令。而blocked状态的线程在等待获取锁,它暂时无法执行,所以操作系统的线程调度器不会将CPU时间片分配给它。
2.2 waiting
2.2.1 含义与触发条件
线程在同步方法中主动调用了wait()方法后会处于waiting状态,此后它通常在等待另一个线程执行特定操作来唤醒它。最常见的情况是线程调用了Object.wait()、Thread.join()或者LockSupport.park()方法。这些方法会导致线程释放它持有的锁(如果有的话),并且暂停执行,进入waiting状态。
2.2.2 CPU 时间片占用情况
处于waiting状态的线程不会占用CPU时间片。因为线程已经主动暂停了自己的执行,等待某个条件被满足或者被其他线程唤醒。操作系统的线程调度器会将CPU时间片分配给其他处于runnable状态的线程,而不是这些处于等待状态的线程。例如,在生产者 - 消费者模型中,消费者线程在发现队列为空时调用Object.wait()方法进入waiting状态,此时它不会占用 CPU 资源,直到生产者线程生产了新的物品并调用Object.notify()或Object.notifyAll()方法唤醒消费者线程。
2.3 timed_waiting
2.3.1 状态含义及触发条件
timed_waiting状态类似于waiting状态,但有一个时间限制。线程进入这个状态通常是因为调用了带有超时参数的等待方法,如Object.wait(long timeout)、Thread.sleep(long millis)、LockSupport.parkNanos(long nanos)或者LockSupport.parkUntil(long deadline)。这些方法会使线程暂停执行一段特定的时间。
2.3.2 CPU 时间片占用情况
处于timed_waiting状态的线程同样不会占用 CPU 时间片。在等待时间结束之前,线程调度器会将 CPU 资源分配给其他可运行的线程。例如,当一个线程调用Thread.sleep(1000)(睡眠 1 秒)时,它会进入timed_waiting状态,在这 1 秒内,它不会占用 CPU 时间片,其他线程可以利用这些时间片进行执行。一旦睡眠时间结束,线程会从timed_waiting状态转换为runnable状态,此时就有机会再次获得 CPU 时间片并继续执行。
3 关于sleep()、wait()、yield()
这三个方法都能让出CPU时间片,似乎作用一样,但是又有什么区别呢。
- 产生的状态不同
sleep()会将线程进入到timed_waiting状态(因为sleep()必须带上时间参数);
wait()会将线程进入到waiting或timed_waiting状态(因为wait()方法有带时间和不带时间参数两种);
yield()会将线程依然保持在runnable状态,因为它不保证会让出CPU。 - 对锁的处理
sleep()不会释放锁;
wait()会释放锁;
yield()不会释放锁。 - 调用位置
sleep()和yield()可以在任何地方调用
wait()必须在同步块中调用 - 唤醒方式:
sleep()时间到自动唤醒;
wait()需要notify()/notifyAll()唤醒;
yield()因为不保证让出CPU时间片,也就不确定什么时候唤醒,但是自动唤醒的。 - 使用场景:
sleep():暂时暂停执行;
wait():线程间协作;
当线程执行不急迫时让出CPU。
4 总结
Java 线程的 blocked、waiting、timed_waiting 状态各有触发条件与特性。sleep、wait、yield 虽都可让 CPU,但在状态变化、锁处理、调用位置、唤醒方式及使用场景上均有差异,合理运用可优化多线程程序的同步、协作与资源利用。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。