一、wait和notify(Synchronized)
- Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态
- BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片
- BLOCKED 线程会在 Owner 线程释放锁时唤醒
- WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入 EntryList 重新竞争
sleep(long n)和wait(long n)的区别
- sleep是Thread方法,而wait是Object的方法
- sleep不需要强制和synchronized配合使用,但wait需要和Synchronized一起用
- sleep在睡眠的同时,不会释放对象锁,而wait在等待的时候会释放对象锁
- 调用后线程状态都为TIMED_WAITING,wait中途被唤醒或者等待时间结束会进入BOLCKED(EntryList)重新竞争锁,但sleep结束后直接进入RUNNABLE状态,因为并没有释放掉锁,CPU可以直接使其运行(如果时间片轮到,或者有多余的线程核)。
二、join和yield
join的本质是让调用线程(执行t1.join()的线程)wait在当前线程对象实例(t1线程)上。调用线程在当前线程对象上进行等待,当线程执行完成后,被等待的线程就会在退出前调用notifyAll()通知所有的等待线程继续执行。因此,不应再Thread实例上使用类似wait()和notify()等方法,会影响系统API工作。
调用者轮询检查alive状态:
t1.join();
等价于下面代码
synchronized (t1) {
// 调用者线程进入 t1 的 waitSet 等待, 直到 t1 运行结束
while (t1.isAlive()) {
t1.wait(0);
}
}
yield会使当前线程让出CPU,会让当前线程从Running进入Runnable状态。但要注意,让出CPU并不代表当前线程不执行了。当前线程让出CPU之后,还会进行CPU资源的争夺,但是否能再次被分配到就不一定了。因此,对Thread.yield()的调用好像就是再说:我已经完成了一些做重要的工作了,我应该是可以休息一下了,给其他线程一些工作机会。
三、park和unpark
LockSupport类使用类似信号量的机制。他为每个线程准备了一个许可(初始值默认为0,不可用),如果许可可用,那么park()会立即返回,并且消费这个许可(也就是将许可变为不可用),如果许可不可用,就会阻塞。
而unpark()则使得一个许可变为可用(但是和信号量不同的是,许可不能增加,你不可能拥有超过一个许可,它永远只有一个)
这个特点使得,即使unpark()操作发生在park()之前,它也可以使下一次的park()立即返回。
与Object的wait ¬ify相比
- wait,notify和notifyAll必须配合Object Monitor(synchronized)一起使用,而unpark不必
- park & unpark是以线程为单位来【阻塞】和【唤醒】线程,而notify只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,就不那么【精确】
- park & unpark可以先unpark,而wait & notify不能先notify
LockSupport.park()还能支持中断影响。但是和其他接收中断的函数很不一样,LockSupport.park()不会抛出InterruptedException异常。它只会默默返回,但会标记中断位。我们可以从Thread.intterrupted()等方法获得中断标记。