java多线程学习(六) 之 Condition

和synchronized一样,Lock是为了线程互斥而设计的,在synchronized中,使用对象隐式锁的wait notify(notifyAll)来实现线程之间的通信,同样的功能在Lock中是通过Codition来实现的。Condition除了完成wait和notify的基本功能之外,它的好处在于一个Lock可以通过创建多个Condition,选择性的去通知wait的线程。

官方解释:
Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.

Condition把隐式锁的wait notify notifyAll功能提取出来,赋予确切的对象,让一个对象有个多等待集(wait-sets),这些condition也是和Lock实例绑定的,换句话说,Lock对象代替了synchronized,condition代替了wait、notify(notifyAll)等方法。
因为一个Lock可以生成多个Condition,所以condition可以让所有的因Lock等待的线程分成几个相互等待的子集合,也就是前面提到的wait-sets.

Conditions (also known as condition queues or condition variables) provide a means for one thread to suspend execution (to “wait”) until notified by another thread that some state condition may now be true. Because access to this shared state information occurs in different threads, it must be protected, so a lock of some form is associated with the condition. The key property that waiting for a condition provides is that it atomically releases the associated lock and suspends the current thread, just like Object.wait.

Condtions(也可以叫condition队列,或者是condition变量),它提供一个方法,让一个线程在其它活着(condition状态为true)的线程在通知它之前,一直处于等待状态。因为不同的线程访问共享资源,必须是在获取锁的情况下。所以Condition的使用要在Lock内。这就是condition的一个重要特性,原子的释放锁,并且挂起线程。和Object.wait效果一样。

官方示例:

官方示例的场景是一个关于生产者消费者的模式。有多个生产者和消费者。但是只有一个仓库。
首先我们要保证在同一时间只有一个人在生产或者消费。
其次我们希望当一个消费者在消费完最后一个物品的时候,去通知任意一个生产者来生产,同样,我们希望当一个生产者把仓库填满的时候,通知任意一个在等待的消费者来消费,而不会通知其它生产者继续生产。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 
 
   final Object[] items = new Object[100];
   int putptr, takeptr, count;
 
   public void put(Object x) throws InterruptedException {
     lock.lock(); //锁1
     try {
       while (count == items.length)
         notFull.await();//锁2
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count; //生产
       notEmpty.signal(); //通知1
     } finally {
       lock.unlock();
     }
   }
 
   public Object take() throws InterruptedException {
     lock.lock();//锁3
     try {
       while (count == 0)
         notEmpty.await();//锁4
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();//通知2
       return x;//消耗
     } finally {
       lock.unlock();
     }
   }
 }

官方示例,即展示了Condition实现了隐式锁的wait notify notifyAll的功能,也包含了condition的不同点。

假设有多个线程在分别调用take方法和put方法。假设所有的线程都挂在了锁1、锁2、锁3、锁4的地方。

只有一个线程到达了通知1的地方, 它只会通知到锁1、锁3 和因同一condition而等待的锁4的地方,继续往下执行。而不会通知锁2。

这样就保证了在items在生产之后只会去通知等待消耗线程去消耗。同样的,在items消耗光之后,只会去通知生产线程去生产, Lock也同时保证了生产消耗不能同时进行。

如果我们使用Object.wait和Object.notify来实现上面的方案。item空了的时候,假设恰巧每次唤醒的都是消费者线程,这个生产消费的过程就无法继续下去了。

留言

提示:你的email不会被公布,欢迎留言^_^

*

验证码 *