java 使用condition在锁中实现多条件

是什么?为什么?怎么做?

 

一、是什么

在java并发中,Lock替代了synchronized方法和语句的使用,Condition替代了Object 监视器方法的使用。

Condition将Object监视器方法(wait、notify和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待set(wait-set)。

ReentrantLock和Condition是配合着使用的,就像Object对象提供的wait和notify方法一样,Condition提供一种多线程间通信机制。

也就是说,Condition起到了充当线程的监视器、控制线程状态的作用。

二、为什么

在想到java并发的概念时,最先想到的是Synchronized,使用锁来实现线程的同步。

只是实现同步是不够的,线程之间还需要通信(例如:线程A完成了工作,使用notifyAll唤醒其他线程(无法唤醒指定的某条线程),继续下面的工作)。所以,Object类中提供了一系列的方法(如wait、notify和notifyAll),作为线程之间通信的途径。

因为诸多原因(因为涉及很多实现原理,这里不再展开),Lock逐渐取代Synchronized,这时候,Object提供的线程通信的方法已经不能满足需求了(或者说是有其短板),所以提出了Condition这样的解决方案。

为什么说满足不了需求?举一个例子:

现在,需要使用多线程读/写同一个缓冲区。当向缓冲区中写入数据之后,唤醒”读线程”;当从缓冲区读出数据之后,唤醒”写线程”。

现在,向缓冲区写入数据完成之后,需要唤醒”读线程”。但是我们不能通过notify()或notifyAll()来明确的指定唤醒某条”读线程”,而只能通过notifyAll()唤醒所有线程(因为现有的方法无法区分唤醒的线程是读线程,还是写线程)。

这就很尴尬了,我们明明只需要唤醒某一条线程即可,但是却不得不花费更多的开销来使用notifyAll(),唤醒所有线程。

所以Condition出现了。它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition(就像多个监视器)。在不同的情况下可以使用不同的Condition。

在这里,线程分为读线程和写线程,我们可以定义两个Condition(相当于两个阻塞队列),如果读线程执行完毕,那么阻塞读线程的Condition,并且唤醒写线程的Condition。这样就能明确的指定唤醒读线程,而不用所有线程一起唤醒了。

我之前尝试过Lock配合我Object的通信方法,起不了任何作用。所以个人认为,synchronized需要搭配Object的通信方法,而Lock需要搭配Condition。这两对是固定的组合。

三、怎么做

之前的生产者-消费者例子是典型的synchronized配合Object的通信方法:

如果改为Lock配合Condition,如下:

在这里,有两种情况,一是事件队列满了,需要阻塞生产者线程,唤醒消费者线程。二是事件队列空了,需要阻塞消费者线程,唤醒生产者线程。因此,可以定义两个Condition,分别代表两种情况:

重点是生产和消费两个方法:

(1)setEvent方法

在生产之前,当然先看看事件队列满了没有:

如果满了,那么就得想办法阻塞生产者线程,并且唤醒消费者线程:

emptyCondition是事件队列为空的情况,既然为空,那就要继续生产,生产者线程可以继续生产事件。因为当前事件列表已经满了,我们想要阻塞生产者线程,就是要让emptyCondition进入等待状态。

既然已经生产了一个事件,也就是说事件列表中至少存在一个事件,那么就可以唤醒消费者线程进行消费。fullCondition是事件队列满了的状态,既然满了,那么就要进行消费。所以说,可以唤醒fullCondition。

(2)getEvent方法

在消费之前,当然先看看事件队列空了没有:

如果空了,那么就得想办法阻塞消费者线程,并且唤醒生产者线程:

fullCondition是事件队列满了的情况,既然满了,那么就要消费,消费者线程可以继续消费。因为当前事件列表已经空了,我们想要阻塞消费者线程,就是要让fullCondition进入等待状态。

既然消费了一个事件,也就是说事件列表已经不为满了,可以唤醒生产者线程生产一个事件。emptyCondition是事件列表为空的状态,既然为空,就可以生产一个事件。所以说,可以唤醒emptyCondition。

上面的逻辑可能有一点绕,但是基本上整个思考过程就是这样的。

四、总结

我个人认为,想要写好Lock配合Condition,一定要很好地使用面向对象的思想(想好整个问题情景大概要怎么去解决),合理分析问题(什么地方要上锁,有哪几种Condition),列出所有情况,才能较好地实现同步。

发表评论

电子邮件地址不会被公开。 必填项已用*标注