java - 强制执行打印顺序,但线程在一次迭代后彼此等待

标签 java multithreading wait notify

我无法解决一个线程在等待另一个问题的麻烦(即使在查看了几个问题后也是如此)。这是我要尝试做的事情:我希望一个线程(称为子线程)在外循环下进行2次迭代打印10次。然后另一个(boss线程)在外循环下进行2次迭代打印100次,前提是子线程排在第一位。它看起来像这样:

Sub Thread- iter = 1
Sub Thread- iter = 2
...
Sub Thread- iter = 10
Boss Thread- iter = 1
Boss Thread- iter = 2
...
Boss Thread- iter = 100

此子线程和老板线程的打印顺序将连续2次(外循环)。
我的实现产生了无法预料的结果,即它打印出子线程的第一次迭代10次然后停止在那里,或者它很少打印出所有语句并通过内循环和外循环运行。我使用wait()notify()启用线程之间的通信。我不确定是否将synchronized块放置在错误的位置,或者只是误用了wait()notify()对。这是代码:
public class Main {

    public static void main(String[] args) {
        Main ic = new Main();

        Thread t1 = new Thread(ic.new Th1(), "Boss Thread-");
        Thread t2 = new Thread(ic.new Th2(), "Sub Thread-");

        t2.start();
        t1.start();
    }

    // Boss Thread
    private class Th1 implements Runnable {

        @Override
        public void run() {
            System.out.println("TH1 RUNS FIRST");
            synchronized (Main.class) { // lock outside of outer loop so
                                                        // boss thread can pick up the next iteration
                for (int i = 0; i < 2; i++) { 
                    // wait, let the sub-thread run first 
                    try {
                        Main.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    // print iterations 
                    for (int j = 0; j < 100; j++) {
                        System.out.println(Thread.currentThread().getName() + " iter = " + (j + 1));
                    }
                    System.out.println("end of boss outer----------------------" + (i + 1));
                    // wake up sub-thread and let it knows inner-iteration finished 
                    Main.class.notify();
                }
            }
        }
    }

    // Sub Thread
    private class Th2 implements Runnable {

        @Override
        public void run() { 
            for (int i = 0; i < 2; i++) {
                synchronized (Main.class) { // lock up Th2
                    // print iterations 
                    for (int j = 0; j < 10; j++) { 
                        System.out.println(Thread.currentThread().getName() + " iter = " + (j + 1)); 
                    }

                    // wake up other boss thread and let it know inner-iteration finished
                    Main.class.notify();

                    // wait for other thread to run
                    try {
                        Main.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("end of Sub outer---------------------- " + (i + 1));
                }
            }
        }
    }
}

需要额外的帮助:有人可以告诉我synchronized是锁定共享资源的好方法还是其他更好的选择?说ReentrantLock吗?另外,notify()wait()是实现线程间通信的好方法,还是有更好的方法来使这种错误更容易发生并且效率更高?

最佳答案

经过一番思考和努力,我提出了一个“实现”,可以“满足”我的期望,如问题帖中所述。我对waitnotify对进行了一些更改,并添加了isTh2RunFirst标志来协助两个线程的通信。这是代码:

public class InterThCom {
    // flag default to false for checking if sub-thread 
    // gets the lock first
    private boolean isTh2RunFirst = false; 

    public static void main(String[] args) {
        InterThCom itc = new InterThCom(); 

        Thread t1 = new Thread(itc.new Th1(), "Boss-thread-"); 
        Thread t2 = new Thread(itc.new Th2(), "Sub-thread-");

        t1.start();
        t2.start();
    }

    private class Th1 implements Runnable {

        @Override
        public void run() { 
            for (int i = 0; i < 2; i++) { 
                synchronized (InterThCom.class) { // lock up inner-loop

                    // boss-thread gets the lock first 
                    // wait for sub-thread and let it run;
                    // otherwise, skip this check
                    if (isTh2RunFirst == false) {
                        // wait for sub-thread, if boss-thread gets the lock first 
                        try {
                            InterThCom.class.wait();
                        } catch (InterruptedException e1) { 
                            e1.printStackTrace();
                        }
                    } 

                    // print iteration 100 times 
                    for (int j = 0; j < 100; j++) {
                        System.out.println(Thread.currentThread().getName() + " iter-" + (j + 1));
                    }
                    // done printing 100 times

                    // sub-thread should run already at this point 
                    isTh2RunFirst = true;

                    // This print helps split boss-th and sub-th prints
                    System.out.println(Thread.currentThread().getName() + " outer-loop iter:" + (i + 1));

                    // wake up sub-thread 
                    InterThCom.class.notify();

                    // wait for sub-thread 
                    try {
                        InterThCom.class.wait();
                    } catch (InterruptedException e) { 
                        e.printStackTrace();
                    } 
                } 
            }
        } 
    }

    private class Th2 implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 2; i++) { 
                synchronized (InterThCom.class) {
                    // print iteration 10 times 
                    for (int j = 0; j < 10; j++) {
                        System.out.println(Thread.currentThread().getName() + " iter-" + (j + 1));
                    }
                    // done printing 10 times

                    // sub-thread already prints j iteration
                    isTh2RunFirst = true; 

                    // This print helps split boss-th and sub-th prints
                    System.out.println(Thread.currentThread().getName() + " outer-loop iter:" + (i + 1));

                    // wake up boss-thread 
                    InterThCom.class.notify();

                    // wait for boss-thread 
                    try {
                        InterThCom.class.wait();
                    } catch (InterruptedException e) { 
                        e.printStackTrace();
                    } 
                } 
            }
            synchronized (InterThCom.class) {
                // boss-thread is waiting at the last iteration, so wake it up
                InterThCom.class.notify();
            } 
        } 
    }

} 

如果有人想针对这种特定情况或其他一般情况,详细讨论线程通信的“适当”实现(在行业实践和效率方面),我也发布了Code Review post

关于java - 强制执行打印顺序,但线程在一次迭代后彼此等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53235919/

相关文章:

java - 创建多线程java类来处理数据

Java:来自外部 jar 的类在线程中实例化时出现 ClassNotFoundException

Java:具有具体实现的抽象枚举

c++ - 在 Windows 上的 C++ 中使用和创建 wxwidgets 中的线程

java - 如何使用同步线程将数据插入到我的数据库表中

java - 保证并发 Web 服务调用之间数据一致性的最佳执行方法?

python - 正确使用 os.wait()?

java - 如何停止向数据库sqlite添加数据?

java - java中HashMap.containsValue()的时间复杂度是多少?

vba - 延迟 VBA 代码操作而不卡住 Excel