前言
ReentrantLock 类有一个 newCondition()
方法,用于获取 Lock 上的一个条件,还可以多次调用 newCondition()
以获得多个条件,Condition 可用于线程间通信。
ReentrantLock优势(相比synchronized)
特性 | 描述 |
---|---|
中断一个等待获取锁的线程 | lockInterruptibly 方法能够在获得锁的同时保持对中断的响应,当获取到锁的线程被中断时,中断异常会被抛出,同时锁也会被释放 |
给锁加上超时获取时间 | 当在带有时间限制的操作中调用了一个阻塞方法时,它能根据剩余时间来提供一个时限。如果操作不能在指定的时间给出结果,那么就会使程序提前结束。 |
公平性 | 提供一个先进先出的队列,让线程有序等待获取锁 |
使用Condition实现一个ArrayBlockingQueue
功能包括:
- 如果一个线程调用该类的
take()
获取元素时,若集合为空则使调用线程阻塞。直到有其他线程为集合加入新元素。 - 如果一个线程调用该类的
put()
添加新元素时,若集合满了则使调用线程阻塞。直到有其他线程从集合中获取数据。内部成员以及构造方法
默认使用长度为 10 的数组来维护数据集合。定义了一个锁,并且根据锁的lock.newCondition()
创建了两个条件,分别对应集合满和集合空两个条件。 ```java //维护的数据 private final T[] datas; //数据的个数 private int count; //插入取出的索引 private int put_index; private int take_index;
//锁 private final Lock lock = new ReentrantLock(); //定义两个条件,分别为“集合满”和“集合空” private Condition full = lock.newCondition(); private Condition empty = lock.newCondition();
//提供MyArrayBlockingQueue的构造方法,初始化T[]数据 public MyArrayBlockingQueue() { this(10); }
public MyArrayBlockingQueue(int maxSize) { this.datas = (T[]) new Object[maxSize]; }
<a name="9uSWR"></a>
##### put/get方法
```java
public void put(T data){
lock.lock();
try {
if(count == datas.length){
//此时集合已经满了
System.out.println("集合已满,请等待...");
//使调用线程挂起
full.await();
}
//不满则添加新元素
datas[put_index++] = data;
count++;
//此时唤醒等待取数据的线程
empty.signalAll();
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
public T take(){
lock.lock();
try {
if(count == 0){
//此时集合已经空了
System.out.println("集合已空,请等待...");
//使调用线程挂起
empty.await();
}
//不空则取出最后一个数据
take_index = count - 1;
T result = datas[take_index--];
count--;
//此时唤醒等待写数据的线程
full.signalAll();
return result;
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
return null;
}
put 方法中,如果集合满了,就调用 await() 方法使对应的线程释放锁,并且使调用线程阻塞。直到其他线程调用了 take() 方法,并调用了 full.signalAll()
时,该请求线程会被精准唤醒,重新竞争到锁后,代码继续往下执行。
若集合不满,则添加新元素,并且通过 empty.signalAll()
精准唤醒等待取数据的线程。
参考资料
ReentrantLock的Condition的作用以及使用
《Java并发编程实战》