Condition.signal () не проснется случайно - PullRequest
1 голос
/ 28 апреля 2020

У меня есть следующий код, где zero (), even (), odd () должны вызываться тремя отдельными потоками и вызываться в такой последовательности: zero () - odd () - zero ( ) - четное (), пока n не будет достигнуто.

public class ZeroEvenOdd {

  private int n;

  private volatile int start = 1;

  private Lock lock = new ReentrantLock();
  private Condition zero = lock.newCondition();
  private Condition even = lock.newCondition();
  private Condition odd = lock.newCondition();

  public ZeroEvenOdd(int n) {
    this.n = n;
  }

  // printNumber.accept(x) outputs "x", where x is an integer.
  public void zero(IntConsumer printNumber) throws InterruptedException {
    lock.lock();
    try {
      while (start <= n) {
        printNumber.accept(0);
        if (start % 2 == 0) {
          even.signal();
        } else {
          odd.signal();
        }
        zero.await();
        System.out.println("zero awake");
      }
      odd.signal();
      even.signal();
    } finally {
      lock.unlock();
    }
  }

  public void even(IntConsumer printNumber) throws InterruptedException {
    lock.lock();
    try {
      while (start <= n) {
        if (start % 2 != 0) {
          even.await();
        } else {
          printNumber.accept(start++);
          zero.signal();
        }
      }
    } finally {
      lock.unlock();
    }
  }

  public void odd(IntConsumer printNumber) throws InterruptedException {
    lock.lock();
    try {
      while (start <= n) {
        if (start % 2 == 0) {
          odd.await();
        } else {
          printNumber.accept(start++);
          zero.signal();
        }
      }
    } finally {
      lock.unlock();
    }
  }
}

А вот тестовый пример:

public static void main(String[] args) {
    IntConsumer consumer = new IntConsumer() {
      @Override
      public void accept(int value) {
        System.out.println(value);
      }
    };
    ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(4);

    Thread thread1 = new Thread(() -> {
      try {
        zeroEvenOdd.zero(consumer);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
    Thread thread2 = new Thread(() -> {
      try {
        zeroEvenOdd.odd(consumer);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
    Thread thread3 = new Thread(() -> {
      try {
        zeroEvenOdd.even(consumer);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });
    thread1.start();
    thread2.start();
    thread3.start();
  }

В идеале, когда n = 5, он должен печатать 0102030405, однако при запуске тестового примера иногда я печатаю 012030405, что означает, что когда start == 2, нулевое условие не было пробуждено, но вместо этого условие даже пробуждалось. Как это случилось? Если я немного изменил код, добавив отдельный флаг, тогда проблема будет решена:

public class ZeroEvenOdd {
  private int n;

  private volatile int start = 1;

  private volatile int who;
  private Lock lock = new ReentrantLock();
  private Condition zero = lock.newCondition();
  private Condition even = lock.newCondition();
  private Condition odd = lock.newCondition();

  public ZeroEvenOdd(int n) {
    this.n = n;
  }

  // printNumber.accept(x) outputs "x", where x is an integer.
  public void zero(IntConsumer printNumber) throws InterruptedException {
    lock.lock();
    try {
      while (start <= n) {
        if (who!=0) {
          zero.await();
        }
        printNumber.accept(0);
        if (start % 2 == 0) {
          who=2;
          even.signal();
        } else {
          who=1;
          odd.signal();
        }
        zero.await();
      }
      odd.signal();
      even.signal();
    } finally {
      lock.unlock();
    }
  }

   public void even(IntConsumer printNumber) throws InterruptedException {
    lock.lock();
    try {
      while (start <= n) {
        if (who!=2) {
          even.await();
        } else {
          printNumber.accept(start++);
          who=0;
          zero.signal();
        }
      }
    } finally {
      lock.unlock();
    }
  }

  public void odd(IntConsumer printNumber) throws InterruptedException {
    lock.lock();
    try {
      while (start <= n) {
        if (who!=1) {
          odd.await();
        } else {
          printNumber.accept(start++);
          who=0;
          zero.signal();
        }
      }
    } finally {
      lock.unlock();
    }
  }
}

Я просто не вижу механики, лежащей в основе этого явления. Пожалуйста, помогите мне прояснить эту проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...