Java 从三个不同的线程按顺序打印一、二、三?

Java 从三个不同的线程按顺序打印一、二、三?,java,multithreading,concurrency,thread-safety,Java,Multithreading,Concurrency,Thread Safety,我正在研究以下面试问题: 一个进程中有三个线程。第一条线打印1 …第二个打印2 2…第三个打印3 3 ... 没完没了。您如何安排这三个线程以 打印123123 我想出了下面的代码,它使用两个线程打印1 2 1 2,但我无法从第三个线程中找出如何打印数字3的条件 public class PrintOneTwoThree { private static boolean isFirst = true; private static final Object lock = new Obje

我正在研究以下面试问题:

一个进程中有三个线程。第一条线打印1 …第二个打印2 2…第三个打印3 3 ... 没完没了。您如何安排这三个线程以 打印123123

我想出了下面的代码,它使用两个线程打印
1 2 1 2
,但我无法从第三个线程中找出如何打印数字
3
的条件

public class PrintOneTwoThree {
  private static boolean isFirst = true;
  private static final Object lock = new Object();

  public static void main(String[] args) {
    // first thread
    new Thread(() -> {
      try {
        synchronized (lock) {
          for (;;) {
            while (!isFirst) {
              lock.wait();
            }
            System.out.print("1 ");
            isFirst = false;
            lock.notify();
          }
        }
      } catch (InterruptedException ignored) {
      }
    }).start();

    // second thread
    new Thread(() -> {
      try {
        synchronized (lock) {
          for (;;) {
            while (isFirst) {
              lock.wait();
            }
            System.out.print("2 ");
            isFirst = true;
            lock.notify();
          }
        }
      } catch (InterruptedException ignored) {
      }
    }).start();
  }
}

如何有效地解决这类问题?

使用整数计数器,并在除以3时检查余数,每次打印后递增计数器,而不是使用布尔标志。 由于将有多个线程等待同一个锁,因此最好使用
notifyAll

private static int counter = 0;

new Thread(() -> {
        try {
            synchronized (lock) {
                for (;;) {
                    while (counter % 3 != 0) {
                        lock.wait();
                    }
                    System.out.print("1 ");
                    ++counter;
                    lock.notifyAll();
                }
            }
        } catch (InterruptedException ignored) {
        }
    }).start();

//And the same stuff for other two threads just replacing value for remainder in if and value in print

下面是一个通用的工作示例,使用计数器一次为
n
线程授予一个线程权限:

public class PrintOneTwoThree {
    private static int currentTask;
    private static int totalThreads;
    private static final Object lock = new Object();

    public static void main(String[] args) {
        currentTask = 0;
        totalThreads = 3;

        for (int i = 0; i < totalThreads; i++) {
            createThread(i);
        }
    }

    static void createThread(int id) {
        new Thread(() -> {
            try {
                for (;;) {
                    synchronized (lock) {
                        while (currentTask != id) {
                            lock.wait();
                        }

                        System.out.print(id + 1 + " ");
                        currentTask = (currentTask + 1) % totalThreads;
                        lock.notifyAll();
                    }
                }
            }
            catch (InterruptedException ignored) {}
        }).start();
    }
}

几句话:

  • notify()
    适用于双线程版本(因为变量上最多有一个其他线程阻塞),但是如果一个线程退出临界区并通知s条件变量currentTask可用,但错误的线程赢得了获取锁的竞赛,则在3+版本中会死锁。我不确定这里的
    notifyAll()
    设计是否合适,因为只有一个线程可以取得进展,所以它似乎是重新检查条件谓词并使用
    notify()
    的替身

  • 我将(;;)的
    移到了synchronized部分之外,以尽可能缩小线程安全范围。这个例子是人为设计的,因为如果您想要访问单个资源的确切行为,而不需要其他任何东西,那么增加线程的开销是没有意义的——您可以在单个线程中确定地执行它。在一个现实世界的例子中,当线程不阻塞条件变量时,线程将在
    for(;;)
    循环的其他地方执行线程安全工作,因此考虑到这一点进行设计似乎是合乎逻辑的


如何有效地解决此类问题?不要在连续的活动中使用线程。是的,这是正确的,但因为这是一些面试问题,所以我正在尝试解决这个问题。如果你需要按顺序执行三个任务,唯一明智的做法是使用一个线程。其他任何事情都将是低效和丑陋的。顺便说一句,System.out.println是单线程的,所以无论使用多少个线程,每次只能使用一个线程。你能提供一个示例让我更好地理解吗?你能添加一些关于如何工作的解释让我更好地理解吗?当然。我更熟悉非Java语言中的多线程,所以如果我的信息不正确或解释不清楚,请告诉我。
    public class PrintOneTwoThree {

    public static void main(String[] args) {

            Printers sp = new Printers();

            ExecutorService executor = Executors.newFixedThreadPool(3);
            executor.submit(new FirstNumberProducer(sp, 9));
            executor.submit(new SecondNumberProducer(sp , 9));
            executor.submit(new ThirdNumberProducer(sp , 9));
            executor.shutdown();
        }

    }


    class Printers {

        Semaphore first = new Semaphore(1);
        Semaphore second = new Semaphore(0);
        Semaphore third = new Semaphore(0);

        public void printFirstNumber() {
            try {
                first.acquire();
            }catch(InterruptedException exception) {

            }
            System.out.print("1");
                second.release();
        }

        public void printSecondNumber() {
            try {
                second.acquire();
            }catch(InterruptedException exception) {

            }
            System.out.print("2");
            third.release();
        }

        public void printThirdNumber() {
            try {
                third.acquire();
            }catch(InterruptedException exception) {

            }
            System.out.print("3");
            first.release();
        }

    }

    class FirstNumberProducer implements Runnable {

        Printers sp;
        int index;

        FirstNumberProducer(Printers sp , int index) {
            this.sp = sp;
            this.index = index;
        }

        @Override
        public void run() {
            for(int i = 1 ; i <= index ; i = i + 3 ) {
                sp.printFirstNumber();
            }
        }
    }

    class SecondNumberProducer implements Runnable{
        Printers sp;
        int index;

        SecondNumberProducer(Printers sp , int index) {
            this.sp = sp;
            this.index = index;
        }

        @Override
        public void run() {
            for(int i = 2 ; i <= index ; i = i + 3) {
                sp.printSecondNumber();
            }
        }
    }

    class ThirdNumberProducer implements Runnable{
        Printers sp;
        int index;

        ThirdNumberProducer(Printers sp , int index) {
            this.sp = sp;
            this.index = index;
        }

        @Override
        public void run() {
            for(int i = 3 ; i <= index ; i = i + 3) {
                sp.printThirdNumber();
            }
        }
    }
 123123123