Java 条件.信号()赢了';我不能随意醒来

Java 条件.信号()赢了';我不能随意醒来,java,multithreading,locking,Java,Multithreading,Locking,我有下面的代码,其中zero()、偶数()、奇数()应该由三个单独的线程调用,并按这样的顺序调用: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(

我有下面的代码,其中zero()、偶数()、奇数()应该由三个单独的线程调用,并按这样的顺序调用: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();
    }
  }
}
理想情况下,当n=5时,应该打印0102030405,但是,当运行测试用例时,有时会打印012030405,这意味着当start==2时,条件zero没有被唤醒,而是条件甚至被唤醒。这是怎么发生的?如果我通过添加一个单独的标志稍微更改了代码,那么问题就解决了:

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();
    }
  }
}
公共类零偶奇数{
私人int n;
私有易失性int start=1;
私人卫生组织;
private Lock=new ReentrantLock();
私有条件零=lock.newCondition();
私有条件偶数=lock.newCondition();
私有条件奇数=lock.newCondition();
公共零偶数(整数n){
这个,n=n;
}
//接受(x)输出“x”,其中x是一个整数。
public void zero(IntConsumer printNumber)抛出InterruptedException{
lock.lock();
试一试{

while(仅因为您以特定顺序调用
Thread.start
而启动)不能保证线程按该顺序执行。因此,不管您执行了什么-
thread1.start();thread2.start();thread3.start()例如,TyRe3可以先执行,然后THEADR2,然后THELAR1。你只是告诉调度器他们准备好执行,而不是命令执行的顺序。感谢提示。我可以看到第一类是有问题的,因为它没有考虑过代码< 0()的可能性。
在其他两个线程之后执行。但是当n变为2时,这意味着条件0必须等待,并且在方法
odd()
中已发出信号,它如何跳过打印0并从“…此锁不保证任何特定的访问顺序”转到方法
偶数()
不客气,肯定有很多微妙之处需要考虑。这是一个棘手的问题。很好的工作提供了一个MCVE与你的问题,很多新用户不这样做:仅仅因为您按特定顺序调用
Thread.start
,并不保证线程按该顺序执行。因此,不管您如何执行-
thread1.start();thread2.start();thread3.start()例如,TyRe3可以先执行,然后THEADR2,然后THELAR1。你只是告诉调度器他们准备好执行,而不是命令执行的顺序。感谢提示。我可以看到第一类是有问题的,因为它没有考虑过代码< 0()的可能性。
在其他两个线程之后执行。但是当n变为2时,这意味着条件0必须等待,并且在方法
odd()
中已发出信号,它如何跳过打印0并从“…此锁不保证任何特定的访问顺序”转到方法
偶数()
不客气,肯定有很多微妙之处需要考虑。这是一个棘手的问题。很好的工作提供了一个MCVE与你的问题,很多新用户不这样做:
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();
    }
  }
}