为什么不能在 ReentrantLock 的对象上直接调用 await 和 signal 方法。为什么我需要条件?

Ana*_*ria 0 java multithreading locking locks

在旧的同步块中,我们使用相同的对象进行同步,还使用了等待通知方法。所以他们都可以引用同一个锁。说得通。

那么当我使用类 ReentrantLock 时,为什么我不能也使用相同的变量来调用lockunlock以及awaitsignal?为什么我需要创建额外的 Condition 变量?

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

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

    void doSomething() {
        lock.lock();
            //some code
            condition.await();
            //some code
        lock.unlock();
    }
Run Code Online (Sandbox Code Playgroud)

而不是这样:(这种类型的编码不会更合乎逻辑)?

Lock lock = new ReentrantLock();

    void doSomething() {
        lock.lock();
            //some code
            lock.await();
            //some code
        lock.unlock();
    }
Run Code Online (Sandbox Code Playgroud)

编辑:来自文档:一个 Condition 实例本质上绑定到一个锁。 为什么要这样设计?为什么不只有一个 Lock 类型的变量,它会有 await 和信号方法?

Sla*_*law 5

的分离Lock,并Condition可以让你有一个以上的ConditionLock,这是由记录Condition

Condition通过将Object监视器方法 ( wait,notifynotifyAll)与任意实现的使用结合起来,将监视器方法 ( ,和) 分解为不同的对象,以产生每个对象具有多个等待集的效果 [强调]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();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这种方法有两个好处:

  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();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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