持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第26天,点击查看活动详情
DK1.5中引入了java.util.concurrent.locks.Condition接口,用来替代wait/notify。wait/notify大家应该很了解,主要用来解决多线程的协调问题(等待/通知),但是其存在如下几个问题:
早唤醒问题:wait/notify是依赖Object+synchronized来实现,如果存在多个线程wait(),那么通过notify()方法只能唤醒一个线程,而且这个线程不一定是我们想要唤醒的线程,导致运行错误。如果通过notifyAll()方法唤醒,那么所有线程都会唤醒,就会存在不想唤醒的线程被唤醒,导致不必要的线程上下文切换,这也就是早唤醒问题。Object.wait(long)是否为超时唤醒问题。Object.wait(long)方法可能是通过notify唤醒也可能是超时唤醒,但是无法通过这方法区分。JUC中的Condition中await()、signal()、signalAll()分别对应Object中的wait()、notify()、notifyAll()方法。但是Condition更加灵活,并且解决了上述两个问题。
Condition的使用
如代码所示,Condition实例通过Lock.newCondition()方法创建。Object.wait()/notify()要求其执行线程必须持有这些方法所属对象的内部锁,Condition也类似,其需要执行线程持有创建该Condition实例的显示锁(lock)。不同的是lock可以创建多个Condition,用来实现不同类型/条件的线程的等待/通知。如示例代码,NotifyThreadA类型线程通过conditionA实例仅仅用来通知WaitThreadA类型线程。如果使用Object.wait()/notify()只能通知所有持有该对象内部锁且处于wait状态的线程,无法按分类/条件唤醒。
Condition作用
第一节已经说明了,Condition解决wait/notify存在的两个问题,在这一节我们总结一下Condition是如何解决这两个问题的。
解决早唤醒问题:Condition可以通过Lock.newCondition()方法创建多个Condition实例,来实现不同类型/条件线程的等待/唤醒,所以只要使用合理,就不会存在通知到不应该通知的线程而导致的线程资源竞争和不必要的线程上下文切换。Object.wait(long)是否为超时唤醒问题:Condition中提供了awaitUntil(Date)方法,这个方法可以用于实现带超时时间限制的等待,其返回一个boolean值用来区分是等待超时还是被通知唤醒的。true表示被通知唤醒,false表示等待超时唤醒。总结:目前实际应用中推荐使用Condition来代替wait/notify。