首页 > 解决方案 > 对 LeetCode 上的“按顺序打印”问题感到困惑

问题描述

这应该是多线程的一个简单问题:https ://leetcode.com/problems/print-in-order/ “Foo 的同一个实例将被传递给三个不同的线程。线程 A 将调用 first(),线程 B 将调用second(),线程C会调用third()。设计一种机制,修改程序,保证second()在first()之后执行,third()在second()之后执行”,他们给出了这段代码:

public Foo() {}
    public void first(Runnable printFirst) throws InterruptedException {
        // printFirst.run() outputs "first". Do not change or remove this line.
        printFirst.run();
    }
    public void second(Runnable printSecond) throws InterruptedException {
        // printSecond.run() outputs "second". Do not change or remove this line.
        printSecond.run();
    }
    public void third(Runnable printThird) throws InterruptedException {
        // printThird.run() outputs "third". Do not change or remove this line.
        printThird.run();
    }

**似乎我可以使用 Thread.join 解决它,如下所示,但我不明白的是为什么他们将 Runnable 的实例传递给每个方法,以及如何正确地做到这一点,因为下面的代码将打印每条消息两次 - 一次因为 Thread.start() 将调用相应的 run() 方法,并且一次直接调用该方法。我知道这是错误的方法,但是如果我们尝试使用 join 方法,则无法弄清楚什么是正确的解决方案。**

public Foo() throws InterruptedException {
        Runnable r1 = () -> {
            System.out.println("first ");
        };
        first(r1);
        
        Runnable r2 = () -> {
            System.out.println("second ");
        };
        second(r2);
        
        Runnable r3 = () -> {
            System.out.println("third ");
        };
        third(r3);
        
        Thread t1 = new Thread(r1);
        t1.start();
        try {
            t1.join(); // wait for this thread to finish before starting #2
        }
        catch(Exception e) {
            System.err.println("Thread 1 error");
        }
        
        Thread t2 = new Thread(r2);
        t2.start();
        
        try {
            t2.join();
        }
        catch(Exception e) {
            System.err.println("Thread 2 error");
        }
        
        Thread t3 = new Thread(r3);
        t3.start();
        
        try {
            t3.join();
        }
        catch(Exception e) {
            System.err.println("Thread 3 error");
        }
    }```

标签: javamultithreadingrunnable

解决方案


Leetcode 是针对代码挑战的,所以我们不应该给出完整的解决方案,因为那对你来说不会是一个挑战。

所以这里有一个提示:使用两个CountDownLatch对象,一个通知方法second()该方法first()已完成,另一个通知方法third()该方法second()已完成。阅读文档以了解如何使用它。

在您阅读文档时,我建议您阅读包文档,以了解有关可用于处理多线程代码的功能的更多信息。


更新

为了更好地理解挑战,假设 Leetcode 正在使用这样的类来测试Foo该类。

public class Test {
    public static void main(String[] args) throws Exception {
        Foo foo = new Foo();
        Thread t1 = new Thread(() -> call(foo::first, "first,"));
        Thread t2 = new Thread(() -> call(foo::second, "second,"));
        Thread t3 = new Thread(() -> call(foo::third, "third."));
        
        // Start threads out of order, with delay between them, giving each thread
        // enough time to complete, if not adequately coded to ensure execution order.
        t2.start();
        Thread.sleep(500);
        t3.start();
        Thread.sleep(500);
        t1.start();
        
        // Wait for threads to complete
        t2.join();
        t3.join();
        t1.join();
        
        // At this point, the program output should be "first,second,third."
    }
    interface FooMethod {
        public void call(Runnable printFirst) throws InterruptedException;
    }
    private static void call(FooMethod method, String text) {
        try {
            method.call(() -> System.out.print(text));
        } catch (InterruptedException e) {
            System.out.println(e);
        }
    }
}

您无法修改此代码,因为它对您隐藏。您必须以某种方式向类中添加代码,Foo以确保以Runnable正确的顺序调用 3 个对象。

简单地添加Thread.sleep()对 3 种方法的调用不是正确的解决方案,因为无论通过下面的这个测试在线程启动之间添加多长时间的延迟,它都应该运行。

您必须使用某种线程同步功能,例如监视器同步器。


推荐阅读