让线程“暂停”的方式对比

概要

其实对这四种方式都知道,但是有时看到人家代码时就会想为什么要采用这种方式来阻塞呢?用其他的行不行?问题其实是对这四种方式的使用场景不熟悉,我从编程的角度来整理一下。

介绍

yield

Thread.yield()大家都知道,它会放弃当前的CPU时间片,退下来让给别的线程运行,但你看它是没有时间参数的,所以这个线程下次被调度的时间点是不定的。
如果对暂停的时间没有要求,可以使用这个,你并不要期望这个线程能暂停多久。

sleep

Thread.sleep()大家都知道,效果跟Thread.yield()一样,不过sleep可以指定时间,什么时候醒过来继续运行。
JDK1.5开始推荐使用TimeUnit的sleep方法,其实是一样的,只是增强了可读性。

wait / notify

wait 和 notify 是基于对象锁的竞争。大家都知道,synchronized关键让进入临界区的线程获得了该对象的锁,即在Mark Word头写上这个线程的ID(偏向锁),如果其他线程来了,会先检查有没有线程在使用这个锁。
如果要使用wait / notify,你得先有一个临界区,进入临界区的线程有资格,其他的线程需要“暂停”。
所以其实你的操作都是围绕这个对象锁的,你对线程是没有感知的。

park / unpark

LockSupport的park和unpark是基于对线程暂停和唤醒,这听上去有点废话,大家其实可以理解为是对线程的“命令”,确实大多数网上对unpark的翻译是“许可”,即叫你停就停,动就动。
既然你要操作线程,你必然要能够感知到所有相关的线程的。

总结

漏了ReentrantLock/Condition,其实这跟wait / notify差不多,也是基于“对象锁”的;
yield 自己随意“暂停”;
sleep 自己有目的的“暂停”;
wait / notify 基于对象锁做运行或等待;
park / unpark 基于(其他)线程的“命令”,所以它没有一个“锁”的概念;