park/unpark和wait/notify都是Java提供给我们的机制,让我们能够对当前线程是否陷入阻塞进行控制。
park/unpark是LockSupport包中的功能,wait/notify是Java的Object自带的api。
二者都提供了带时间和不带时间版本的api来阻塞/唤醒当前线程。
区别
1. 是否需要持有锁
wait/notify必须在synchronized代码块(持有当前对象锁)中调用,调用wait会释放当前对象的对象锁,直到被其他线程通过notify/notifyAll唤醒.
park/unpark不需要在同步块中调用,因此不需要持有锁。并且park/unpark实现方式是最多数量为1的信号量机制,通过unpark会使得信号量恢复为1,调用park时如果当前信号量数量大于0,就直接放行(信号量数量-1),否则才阻塞当前线程。因为park/unpark并没有持有锁,因此当线程被阻塞时也就不涉及到对象锁释不释放的问题了。
不需要在同步块中调用使得park/unpark机制对比wait/notify来说,是更灵活的一种机制。
2. 对中断的响应处理
当前线程因为调用wait陷入阻塞时,如果当前线程被中断,此时wait阻塞会被唤醒,并抛出InterruptedException.
当线程因为park调用陷入阻塞时,如果当前线程被中断,此时被park阻塞的线程会被唤醒,但不会抛出InterruptedException,调用者需要通过 Thread.isInterrupted来检查当前线程是否被中断了来响应中断。
3. 唤醒信号丢失问题
对于wait/notify来说,如果notify发生在wait之前,这个时候就可能发生唤醒信号丢失,也就是说线程会在wait处陷入阻塞。因此wait/notify需要搭配外部状态变量来使用,避免唤醒信号丢失问题。
对于park/unpark来说,由于实现方式是通过信号量机制来实现的。一旦通过unpark将信号量恢复了,使用park时就不会阻塞,因此唤醒信号不会丢失。
如何选择?
对于简单的生产者-消费者模型,可以直接使用wait/notify,对于比较复杂的同步模型,我们通常可以使用条件变量和其他并发包下的数据结构,直接使用LockSupport的机会一般来说比较少,除非现有同步原语不满足需求,我们需要更精细的控制,这个时候可能会去使用LockSupport。