Java 用两条线一次打印一个字母和数字

Java 用两条线一次打印一个字母和数字,java,multithreading,concurrency,thread-safety,Java,Multithreading,Concurrency,Thread Safety,我正在做下面的面试问题,我需要用两个线程打印出字母和数字。一个打印字母表(a,b,c…z),另一个打印数字(1,2,3…26)。现在我必须以这样一种方式实现它,即输出应该是: a 1 b 2 ... ... z 26 因此,我提出了下面的代码1,没有同步,但出于某种原因,它没有打印最后一个字母,即z class Output { private static final int MAX = 26; private static int count = 1; private stat

我正在做下面的面试问题,我需要用两个线程打印出字母和数字。一个打印字母表(a,b,c…z),另一个打印数字(1,2,3…26)。现在我必须以这样一种方式实现它,即输出应该是:

a
1
b
2
...
...
z
26
因此,我提出了下面的代码1,没有同步,但出于某种原因,它没有打印最后一个字母,即
z

class Output {
  private static final int MAX = 26;
  private static int count = 1;
  private static final Queue<Character> queue = new LinkedList<>(Arrays.asList(new Character[] {
      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
      's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}));
  private boolean isAlphabet = true;

  public void printAlphabet() {
    while (true) {
      if (count > MAX)
        break;
      if (!isAlphabet) {
        System.err.println(Thread.currentThread().getName() + " : " + queue.remove());
        isAlphabet = true;
      }
    }
  }

  public void printNumber() {
    while (true) {
      if (count > MAX)
        break;
      if (isAlphabet) {
        System.err.println(Thread.currentThread().getName() + " : " + count++);
        isAlphabet = false;
      }
    }
  }
}


public class PrintAlphabetNumber {
  public static void main(String[] args) {
    Output p = new Output();
    Thread t1 = new Thread(() -> p.printAlphabet());
    t1.setName("Alphabet");
    Thread t2 = new Thread(() -> p.printNumber());
    t2.setName("Number");

    t1.start();
    t2.start();
  }
}
类输出{
专用静态最终int MAX=26;
私有静态整数计数=1;
private static final Queue Queue=new LinkedList(Arrays.asList)(新字符[]){
‘a’、‘b’、‘c’、‘d’、‘e’、‘f’、‘g’、‘h’、‘i’、‘j’、‘k’、‘l’、‘m’、‘n’、‘o’、‘p’、‘q’、‘r’,
‘s’、‘t’、‘u’、‘v’、‘w’、‘x’、‘y’、‘z’});
私有布尔值isAlphabet=true;
公共空白打印字母表(){
while(true){
如果(计数>最大值)
打破
如果(!isAlphabet){
System.err.println(Thread.currentThread().getName()+“:“+queue.remove());
isAlphabet=true;
}
}
}
公共无效打印编号(){
while(true){
如果(计数>最大值)
打破
如果(isAlphabet){
System.err.println(Thread.currentThread().getName()+“:”+count++);
isAlphabet=假;
}
}
}
}
公共类打印字母编号{
公共静态void main(字符串[]args){
输出p=新输出();
线程t1=新线程(()->p.printAlphabet());
t1.集合名(“字母表”);
线程t2=新线程(()->p.printNumber());
t2.设置名称(“编号”);
t1.start();
t2.start();
}
}
我上面的代码有什么问题吗?同样从同步的角度来看,它看起来好还是不好

出于某种原因,它没有打印最后一个字母,即z

count>MAX
时中止,这在最后一个数字后为真

在最后一个数字之后,您应该打印最后一个字母,但是现在
count>MAX
,所以它已经停止了

从同步的角度来看,它看起来好还是不好

不,这看起来不太好

您正在使用自旋锁。这是非常低效的,因为无论是否有工作要做,两个循环都会持续使用100%的CPU。它也不能保证使用非易失性锁变量

经典的Java解决方案将使用
wait()/notify()

出于某种原因,它没有打印最后一个字母,即z

count>MAX
时中止,这在最后一个数字后为真

在最后一个数字之后,您应该打印最后一个字母,但是现在
count>MAX
,所以它已经停止了

从同步的角度来看,它看起来好还是不好

不,这看起来不太好

您正在使用自旋锁。这是非常低效的,因为无论是否有工作要做,两个循环都会持续使用100%的CPU。它也不能保证使用非易失性锁变量


经典的Java解决方案将使用
wait()/notify()
,就像他们说的那样,这不是一个好的代码,但是这段代码的问题是您将if条件向后设置

public void printAlphabet() {
    while (true) {
        if (count > MAX)
            break;
        if (isAlphabet) {// this was !isAlphabet
            System.err.println(Thread.currentThread().getName() + " : " + queue.remove());
            isAlphabet = false;//also here
        }
    }
}

public void printNumber() {
    while (true) {
        if (count > MAX)
            break;
        if (!isAlphabet) {// this was isAlphabet
            System.err.println(Thread.currentThread().getName() + " : " + count++);
            isAlphabet = true;//also here
        }
    }
}

就像他们说的,这不是一个很好的代码,但是这段代码的问题是你得到了if条件

public void printAlphabet() {
    while (true) {
        if (count > MAX)
            break;
        if (isAlphabet) {// this was !isAlphabet
            System.err.println(Thread.currentThread().getName() + " : " + queue.remove());
            isAlphabet = false;//also here
        }
    }
}

public void printNumber() {
    while (true) {
        if (count > MAX)
            break;
        if (!isAlphabet) {// this was isAlphabet
            System.err.println(Thread.currentThread().getName() + " : " + count++);
            isAlphabet = true;//also here
        }
    }
}

isAlphabet
应声明为
volatile
。您也可以为此使用信号量。您不允许使用锁吗?我也可以使用锁,但在这个特定场景中,我怀疑我们是否需要同步,但我也可以使用同步。@Ivan为什么必须声明为volatile。我知道我们在线程之间共享数据,但您不认为对于这个特殊问题,我们可能不需要同步吗?两个线程都在读取和写入共享变量。它应该同步,以便一个线程所做的更改对另一个线程100%可见
isAlphabet
应该声明为
volatile
。您也可以为此使用信号量。您不允许使用锁吗?我也可以使用锁,但在这个特定场景中,我怀疑我们是否需要同步,但我也可以使用同步。@Ivan为什么必须声明为volatile。我知道我们在线程之间共享数据,但您不认为对于这个特殊问题,我们可能不需要同步吗?两个线程都在读取和写入共享变量。它应该同步,以便一个线程所做的更改现在对anotherMake sense 100%可见。我怎样才能解决这个问题?我认为在这个特定场景中,我们根本不需要锁,但我也愿意使用锁解决方案。如果您有更好或更有效的方法,请让我知道在最简单的情况下,您可以使您的方法同步,并在您更改
isAlphabet
wait()
时添加
notify()
,无论何时检查它,它都不是您想要的值。@zapl获得了它。。您能否在此解决方案的基础上添加答案。另外,如果您能为此添加Java7解决方案,这将非常好,它将帮助我更好地理解。我怎样才能解决这个问题?我认为在这个特定场景中,我们根本不需要锁,但我也愿意使用锁解决方案。如果您有更好或更有效的方法,请让我知道在最简单的情况下,您可以使您的方法同步,并在您更改
isAlphabet
wait()
时添加
notify()
,无论何时检查它,它都不是您想要的值。@zapl获得了它。。您能否在此解决方案的基础上添加答案。另外,如果您能为此添加Java7解决方案,这将非常棒,它将帮助我更好地理解。