当前位置: 首页 > 知识库问答 >
问题:

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

左丘弘致
2023-03-14

我正在回答以下面试问题:

一个进程有三个线程。第一个线程打印1 1 1...,第二个打印2 2 2...,第三个打印3 3 3...无休止。你如何安排这三个线程以打印1 2 3 1 2 3...

我想出了下面的代码,使用两个线程打印1 2 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个答案

翁翰
2023-03-14
    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
司徒鸿文
2023-03-14

每次打印后,使用整数计数器和检查余数除以3和增量计数器来代替布尔标志。由于同一锁上会有几个线程等待,所以最好使用通知所有

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
司空健
2023-03-14

下面是一个使用计数器一次为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();
    }
}

输出:

1 2 3 1 2 3 1 2 3 ...

试试吧!

几句话:

>

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

    我移动了(;)的 在同步部分之外,以尽可能缩小线程安全范围。这个例子是人为设计的,因为如果你想要访问单个资源而不是别的什么,那么增加线程的开销是没有意义的——你也可以在单个线程中确定地执行它。在现实世界的例子中,线程将在(;)的中的其他地方执行线程安全工作 循环,所以考虑到这一点进行设计似乎是合乎逻辑的。

  •  类似资料:
    • 采访中问 有三条线。第一条线打印100到199个数字。第二个线程打印200到299之间的数字。第三条线从300到399。执行的顺序是

    • 这个问题是在电子艺术采访中提出的。 有三条线。第一个线程打印1到10个数字。第二个线程打印从11到20的数字。第三条线从21到30。现在这三个线程都在运行。然而,数字是按不规则的顺序打印的,如1、11、2、21、12等。 如果我想让数字按排序顺序打印,比如1,2。。。我该怎么处理这些线呢?

    • 我试图创建一个实现,其中多个线程打印序列的备用值。这里thread1将打印1,4,7 thread2将打印2,5,8 thread3将打印3,6,9。我用的是原子整数和模函数。 下面的实现可以很好地工作,第一个线程打印1,4,7,第二个线程打印2,5,8,第三个线程打印3,6,9,但问题是没有保持顺序,即输出可能类似于1,3,2,4,5,7,8,6,9,而我希望保持顺序,因为正确的线程可以打印这些

    • 问题内容: 我正在尝试同步三个线程以打印012012012012…。但是它不能正常工作。每个线程都分配有一个编号,当它从主线程接收到信号时将打印该编号。以下程序有问题,我无法捕获。 问题答案: 您需要更多的协调。该notify调用不会立即唤醒线程并强制其继续执行。相反,您可以考虑notify将电子邮件发送给线程以使其可以继续进行。想象一下,如果您想让3个朋友按顺序给您打电话。您向朋友1发送了一封电

    • 有两个线程,一个是打印偶数,另一个是打印奇数。在下面自定义锁的帮助下,我想按顺序打印数字。问题出在打印一些数字后(显示的数字顺序正确。)线程越来越死机。我花了一个多小时还是找不到问题,对我来说一切都很好。

    • NowCoder 题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。 解题思路 // java public ArrayList<arraylist> Print(TreeNode pRoot) { ArrayList<arraylist> ret = new ArrayList<>()