一、概述
任何一个Java对象,都拥有一组监视器方法,主要包括wait()、notify()、notifyAll()方法,这些方法与synchronized关键字配合使用可以实现等待/通知机制。使用这种方式实现了生产者-消费者模式。
类似地,Condition接口也提供类似的Object的监视器的方法,主要包括await()、signal()、signalAll()方法,这些方法与Lock锁配合使用也可以实现等待/通知机制。
1.1、对比object监视器
| 对比项 | Object监视器 | Condition |
|---|---|---|
| 前置条件 | 获取对象的锁 | 调用Lock.lock获取锁,调用Lock.newCondition获取Condition对象 |
| 调用方式 | 直接调用,比如object.notify() | 直接调用,比如condition.await() |
| 等待队列的个数 | 一个 | 多个 |
| 当前线程释放锁进入等待状态 | 支持 | 支持 |
| 当前线程释放锁进入等待状态,在等待状态中不断响中断 | 不支持 | 支持 |
| 当前线程释放锁并进入超时等待状态 | 支持 | 支持 |
| 当前线程释放锁并进入等待状态直到将来的某个时间 | 不支持 | 支持 |
| 唤醒等待队列中的一个线程 | 支持 | 支持 |
| 唤醒等待队列中的全部线程 | 支持 | 支持 |
1.2、Condition接口定义
public interface Condition { // 使当前线程处于等待状态,释放与Condtion绑定的lock锁 // 直到 singal()方法被调用后,被唤醒(若中断,就game over了) // 唤醒后,该线程会再次获取与条件绑定的 lock锁 void await() throws InterruptedException; // 相比较await()而言,不响应中断 void awaitUninterruptibly(); // 在wait()的返回条件基础上增加了超时响应,返回值表示当前剩余的时间 // < 0 ,则表示超时 long awaitNanos(long nanosTimeout) throws InterruptedException; // 同上,只是时间参数不同而已 boolean await(long time, TimeUnit unit) throws InterruptedException; // 同上,只是时间参数不同而已 boolean awaitUntil(Date deadline) throws InterruptedException; // 表示条件达成,唤醒一个被条件阻塞的线程 void signal(); // 唤醒所有被条件阻塞的线程。 void signalAll(); }
Condition的使用小结:
- Condition与Lock配套使用,通过
Lock#newConditin()进行实例化 -
Condition#await()会释放lock,线程阻塞;直到线程中断orCondition#singal()被执行,唤醒阻塞线程,并重新获取lock - 经典case可以参考jdk的阻塞队列实现(ArrayBlockingQueue, LinkedBlockingQueue)
后续可以分析:ArrayBlockingQueue, LinkedBlockingQueue实现
1.3、使用Condition接口配合Lock锁的使用实例如下
Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void conditionWait() throws InterruptedException { lock.lock(); try { //.... condition.await(); }finally { lock.unlock(); } } public void conditionSignal(){ lock.lock(); try { //... condition.signal(); }finally { lock.unlock(); } }
一般而言,都会将Condition变量作为成员变量。当调用await方法后,当前线程会释放锁并进入Condition变量的等待队列,而其他线程调用signal方法后,通知正在Condition变量等待队列的线程从await方法返回,并且在返回前已经获得了锁。
1.4、示例
package com.lhx.cloud.threadlock; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author lihongxu * @desc @link(https://github.com/bjlhx15/common-study) * @since 2019/3/26 下午4:23 */ public class SampleQueue<T> { private Object[] elements; private Lock lock=new ReentrantLock(); //队列是否为空 private Condition notEmpty = lock.newCondition(); //队列是否已满 private Condition notFull = lock.newCondition(); private int length=0,addIndex=0,removeIndex=0; public SampleQueue(int size) { this.elements = new Object[size]; } public int getLength() { return length; } public void add(T object) throws InterruptedException { lock.lock(); try { while (length==elements.length){ System.out.println("队列已满,等待~~~"); notFull.await(); } elements[addIndex]=object; if(addIndex++ ==elements.length){ addIndex = 0; } length++; notEmpty.signal(); } finally { lock.unlock(); } } public T remove() throws InterruptedException { lock.lock(); try { while (length==0){ System.out.println("队列为空,等待~~~"); notEmpty.await(); } Object element = elements[removeIndex]; if(removeIndex++ ==elements.length){ removeIndex = 0; } length--; notFull.signal(); return (T) element; } finally { lock.unlock(); } } }