为什么不能在ReentrantLock的对象上直接调用信号方法。为什么需要条件?

时间:2020-08-09 22:34:52

标签: java multithreading locking locks

在旧的同步块中,我们使用相同的对象进行同步,还使用了 wait notify 方法。因此他们都可以引用相同的锁。有道理。

因此,当我使用ReentrantLock类时,为什么不能同时使用相同的变量来调用 lock unlock 以及 await 信号?为什么我需要使其他Condition变量变?

这就是为什么我需要这样做:

Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    void doSomething() {
        lock.lock();
            //some code
            condition.await();
            //some code
        lock.unlock();
    }

代替这个:(这种编码是否更逻辑)?

Lock lock = new ReentrantLock();

    void doSomething() {
        lock.lock();
            //some code
            lock.await();
            //some code
        lock.unlock();
    }

编辑:来自文档:条件实例本质上绑定到锁。 为什么要这样设计?为什么不只有一个Lock类型的变量具有等待和信号方法呢?

1 个答案:

答案 0 :(得分:4)

LockCondition的分隔允许您每个Condition有多个Lock,这由Condition记录:

ConditionObject监视方法(waitnotifynotifyAll)分解为不同的对象,从而具有多个每个对象的等待集 [强调添加] ,方法是将它们与任意Lock实现组合使用。

还有Lock

[{Lock实现]允许更灵活的结构,可能具有完全不同的属性,并且可能支持多个关联的Condition对象 [强调] >。

借助该功能,您可以执行以下操作:

import java.util.Objects;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Stack<E> {

  private final Lock lock = new ReentrantLock();
  private final Condition notEmpty = lock.newCondition();
  private final Condition notFull = lock.newCondition();

  private final Object[] elements;
  private int size;

  public Stack(int capacity) {
    elements = new Object[capacity];
  }

  public E pop() throws InterruptedException {
    lock.lockInterruptibly();
    try {
      while (size == 0) {
        notEmpty.await();
      }
      @SuppressWarnings("unchecked")
      E element = (E) elements[--size];
      elements[size] = null;
      notFull.signal();
      return element;
    } finally {
      lock.unlock();
    }
  }

  public void push(E element) throws InterruptedException {
    Objects.requireNonNull(element);
    lock.lockInterruptibly();
    try {
      while (size == elements.length) {
        notFull.await();
      }
      elements[size++] = element;
      notEmpty.signal();
    } finally {
      lock.unlock();
    }
  }
}

这种方法有两个好处:

  1. 当推入一个元素时,仅发出等待弹出一个元素的线程的信号,反之亦然。换句话说,只有等待特定Condition的线程才被发信号。
  2. 您不必调用signalAll(),这意味着只唤醒了一个线程。
  3. (奖金),至少在我看来,提高了代码的可读性。

这里是相同的Stack类,但使用的是synchronized

import java.util.Objects;

public class Stack<E> {

  private final Object lock = new Object();

  private final Object[] elements;
  private int size;

  public Stack(int capacity) {
    elements = new Object[capacity];
  }

  public E pop() throws InterruptedException {
    synchronized (lock) {
      while (size == 0) {
        lock.wait();
      }
      @SuppressWarnings("unchecked")
      E element = (E) elements[--size];
      elements[size] = null;
      lock.notifyAll();
      return element;
    }
  }

  public void push(E element) throws InterruptedException {
    Objects.requireNonNull(element);
    synchronized (lock) {
      while (size == elements.length) {
        lock.wait();
      }
      elements[size++] = element;
      lock.notifyAll();
    }
  }
}

现在请注意,每个线程必须在相同的“条件”上等待,并且在发生任何事情时会通知每个等待的线程。您必须通知所有等待中的线程,因为您无法更好地控制要通知哪些线程。