前言

ReentrantLock 类有一个 newCondition() 方法,用于获取 Lock 上的一个条件,还可以多次调用 newCondition() 以获得多个条件,Condition 可用于线程间通信。

ReentrantLock优势(相比synchronized)

特性 描述
中断一个等待获取锁的线程 lockInterruptibly 方法能够在获得锁的同时保持对中断的响应,当获取到锁的线程被中断时,中断异常会被抛出,同时锁也会被释放
给锁加上超时获取时间 当在带有时间限制的操作中调用了一个阻塞方法时,它能根据剩余时间来提供一个时限。如果操作不能在指定的时间给出结果,那么就会使程序提前结束。
公平性 提供一个先进先出的队列,让线程有序等待获取锁

使用Condition实现一个ArrayBlockingQueue

功能包括:

  1. 如果一个线程调用该类的 take() 获取元素时,若集合为空则使调用线程阻塞。直到有其他线程为集合加入新元素。
  2. 如果一个线程调用该类的 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]; }

  1. <a name="9uSWR"></a>
  2. ##### put/get方法
  3. ```java
  4. public void put(T data){
  5. lock.lock();
  6. try {
  7. if(count == datas.length){
  8. //此时集合已经满了
  9. System.out.println("集合已满,请等待...");
  10. //使调用线程挂起
  11. full.await();
  12. }
  13. //不满则添加新元素
  14. datas[put_index++] = data;
  15. count++;
  16. //此时唤醒等待取数据的线程
  17. empty.signalAll();
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }finally{
  21. lock.unlock();
  22. }
  23. }
  24. public T take(){
  25. lock.lock();
  26. try {
  27. if(count == 0){
  28. //此时集合已经空了
  29. System.out.println("集合已空,请等待...");
  30. //使调用线程挂起
  31. empty.await();
  32. }
  33. //不空则取出最后一个数据
  34. take_index = count - 1;
  35. T result = datas[take_index--];
  36. count--;
  37. //此时唤醒等待写数据的线程
  38. full.signalAll();
  39. return result;
  40. } catch (Exception e) {
  41. e.printStackTrace();
  42. }finally{
  43. lock.unlock();
  44. }
  45. return null;
  46. }

put 方法中,如果集合满了,就调用 await() 方法使对应的线程释放锁,并且使调用线程阻塞。直到其他线程调用了 take() 方法,并调用了 full.signalAll() 时,该请求线程会被精准唤醒,重新竞争到锁后,代码继续往下执行。
若集合不满,则添加新元素,并且通过 empty.signalAll() 精准唤醒等待取数据的线程。

参考资料

ReentrantLock的Condition的作用以及使用
《Java并发编程实战》