线程间协作
等待与通知:wait/notify
wait方法:一个线程因其执行目标动作所需的保护条件未满足而被暂停的过程。wait方法继承自Object类(方法修饰符为fianl native,这也解释了为什么condition类中不能重写wait等方法)
-
阻塞:wait方法的调用都会使当前线程阻塞。该线程将会被放置到对该Object的请求等待队列中,然后让出当前对Object所拥有的对象锁。线程会一直暂停所有线程调度,直到下面其中一种情况发生:
① 其他线程调用了该Object的notify方法,而该线程刚好是那个被唤醒的线程;
② 其他线程调用了该Object的notifyAll方法;
③ 其他对象中断/杀死了该线程;
④ (这种情况,只针对前两个方法)线程在等待指定的时间后;
-
恢复:线程将会从等待队列中移除,重新成为可调度线程。它会与其他线程以常规的方式竞争对象锁。一旦它重新获得对象的锁,所有之前的请求状态都会恢复,也就是线程调用wait的地方的状态。线程将会在之前调用wait的地方继续运行下去。
为什么wait方法必须放在临界区中使用?
由于一个线程只有在持有一个对象的内部锁的情况下才能够调用该对象的wait方法。
通知notify和notifyAll方法:一个线程更新了系统的状态,使得其他线程所需的保护条件得以满足的时候唤醒那些被暂停的线程的过程。notify的作用就是唤醒请求队列中的一个线程,而notifyAll唤醒的是请求队列中的所有线程。
- 被唤醒的线程不会马上运行,除非获取了该Object的锁。调用notify的线程,在调用notify后,不会像wait一样,马上阻塞线程的运行。而是继续运行,直到相应的线程调度完成或者让出Object的锁。而被唤醒的线程会在当前线程让出Object锁后,与其他线程以常规的方式竞争对象锁(正如上面提到的)。
- 选择:
- notify可能导致信号丢失的问题(如果等待队列中线程有多个,notify只会随机的唤醒一个)
- notifyAll会把不需要唤醒的等待线程都唤醒了,但是正确性有保障(不需要唤醒的等待线程被唤醒,会导致上下文切换)
- 使用notify代替notifyAll的条件:
- 一次通知仅需要唤醒至多一个线程。
- 相应对象上的所有等待线程都是同质等待线程。
wait/notify的开销与问题:
- 过早唤醒问题:
- 产生条件,多个线程竞争一个锁,但不同线程的保护条件不同,导致一个线程因为自己的保护条件成立,唤醒了锁对象中等待队列的所有线程。其他保护条件不成立的线程被过早唤醒。(该情况,只能使用while循环判断保护条件,防止唤醒的线程不是保护条件对应的线程)
- 副作用,引起上下文切换 。
- 信号丢失问题:等待线程错过了一个本来发送给它的信号,导致等待线程一直处于等待状态。
- 判断保护条件的方式不对,使用if判断保护条件。规避:要将对保护条件的判断和Object.wait()调用放在一个循环语句之中就可以避免信号丢失。
- 使用Object.notify()通知具有多个线程的等待队列。规避:如果有多个线程在等待队列中,使用Object.notifyAll()来通知。
- 总的来说,信号丢失本质上是一种代码错误,而不是java标准库API自身的问题。
- 欺骗性唤醒问题:等待线程也可能在没有其他任何线程执行Object.notify()/notifyAll()的情况下被唤醒。由于操作系统是允许这种现象产生,因此java平台同样允许这种现象的存在。
- 上下文切换 :wait/notify的使用可能导致较多的上下文切换。
- 减少wait/notify导致过多的上下文切换。
- 在保证程序正确性的前提下,使用Object.notify()代替Object.notifyAll().
- 通知线程在执行完Object.notify()/notifyAll()之后尽快释放相应的内部锁。这样可以避免被唤醒的线程在Object.wait()调用返回前再次申请相应的内部锁时,由于该锁尚未被通知线程释放而导致该线程被暂停。
- 减少wait/notify导致过多的上下文切换。
等待与通知:wait/notify是一种非公平的调度方式,等待线程和通知线程是同步在同一对象之上的两种线程。
Thread.join( )
- 使用wait和notify配合实现
- 当前线程调用,则其它线程等待当前线程执行完毕
Java 条件变量
Condition接口可作为wait/notify的代替品来实现等待/通知,它为解决过早唤醒问题提供了支持,对应方法包括await、singal和singalAll方法 。
- Condition 需要与显式锁配合使用。Condition.await/singal也要求执行线程持有创建该Condition实例的显示锁。
- Lock.newCondition( )的返回值就是一个Co