【问题标题】:Java thread synchronization gives weird outputJava 线程同步给出了奇怪的输出
【发布时间】:2020-12-29 23:22:29
【问题描述】:

我的目标是一个线程打印奇数,另一个线程打印偶数。 下面的实现显示了奇怪的输出。请让我知道我在这段代码中缺少什么。

它按预期打印 0 和 1。然后打印 3 和 2。

// 预期 -> 012345678910

//此代码实际输出-> 013254769810(流量正在变化)

public class OddEvenNumberPrint {
    public static boolean state = false; // based on this flag I will wait or notify
    public static Object lock = new Object(); // the common locking object
    
    public static void main(String[] args) {
        EvenThread t1 = new EvenThread("Even");
        t1.start();
        OddThread t2 = new OddThread("Odd");
        t2.start();

    }

}

class EvenThread extends Thread {
    

    public EvenThread(String name) {
        this.setName(name);
    }

    public void run() {

        for (int i = 0; i <= 10; i = i + 2) {
            synchronized (OddEvenNumberPrint.lock) {
                System.out.println("Even ->"+i);
                while (!OddEvenNumberPrint.state) {
                    try {
                        System.out.println(Thread.currentThread().getName()+ " : "+ "Going wait");
                        OddEvenNumberPrint.lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                OddEvenNumberPrint.state = false;
                OddEvenNumberPrint.lock.notify();
                System.out.println(Thread.currentThread().getName()+ " : "+ "Released");

            }
        }

    }
}

class OddThread extends Thread {
    

    public OddThread(String name) {
        this.setName(name);
    }

    public void run() {
        for (int i = 1; i < 10; i = i + 2) {
            synchronized (OddEvenNumberPrint.lock) {
                System.out.println("Odd -> "+i);
                while (OddEvenNumberPrint.state) {
                    try {
                        System.out.println(Thread.currentThread().getName()+ " : "+ "Going wait");
                        OddEvenNumberPrint.lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                OddEvenNumberPrint.state = true;
                OddEvenNumberPrint.lock.notify();
                System.out.println(Thread.currentThread().getName()+ " : "+ "Released");
            }
        }
    }
}

输出:

Even ->0
Even : Going wait
Odd -> 1
Odd : Released
Odd -> 3
Odd : Going wait
Even : Released
Even ->2
Even : Going wait
Odd : Released
Odd -> 5
Odd : Going wait
Even : Released
Even ->4
Even : Going wait
Odd : Released
Odd -> 7
Odd : Going wait
Even : Released
Even ->6
Even : Going wait
Odd : Released
Odd -> 9
Odd : Going wait
Even : Released
Even ->8
Even : Going wait
Odd : Released
Even : Released
Even ->10
Even : Going wait

【问题讨论】:

  • 请解释您认为输出“奇怪”的地方,以及您认为它奇怪的原因。另请解释您的预期。
  • 你期待什么输出?!!
  • 1.提示:您的程序不会终止。 (在可接受的期限内) 2. 提示:不要使用局部变量进行计数,而是使用“同步”变量。
  • @MarkRotteveel 和 Omid,我已经给出了正确的细节。
  • 没有什么能阻止奇数循环打印 1,然后在偶数线程再次轮到它之前打印 3。如果您想要您期望的顺序,您可能需要将state 初始化为true,并将值的打印移动到循环之后(尽管我没有仔细查看)。您似乎期望notify() 会立即给另一个线程运行的机会,但事实并非如此。

标签: java multithreading synchronization thread-safety wait


【解决方案1】:

考虑:

  1. 因为even线程是在odd线程之前创建和启动的,所以意味着even肯定会先获得互斥锁(lock),NO! odd 也有机会在 even 之前获得它。所以可能有1 然后0
  2. 状态管理是多线程中的关键,您的问题就在于此。必须在正确的时间/点发出信号。

问题

您的代码的问题是信号和状态管理在错误的点,特别是两个线程的起点看起来有问题。

让我们模拟一下跑步:
假设even 线程启动并首先获取lock。 时间 0。even 线程打印 0

             |Even Thread              |Odd Thread               |lock/mutex
-------------+-------------------------+-------------------------+----------------
time 0       |acquires lock of mutex   |blocked, cannot acquire  |false
             |                         |lock of mutex            |
-------------+-------------------------+-------------------------+----------------
time 1       |prints (next even 0,...) |still waiting...         |false
-------------+-------------------------+-------------------------+----------------
time 2       |going to unlock mutex    |acquires lock of mutex   |false
             |and wait for a signal    |as it's unlocked from    |
             |becasue lock == false    |even                     |
-------------+-------------------------+-------------------------+----------------
time 3       |still waiting...         |prints (next odd 1,...)  |false
-------------+-------------------------+-------------------------+----------------
time 4       |still waiting...         |won't wait for a signal  |false
             |                         |since lock is false      |
-------------+-------------------------+-------------------------+----------------
time 5       |still waiting...         |set the lock state to    |true
             |                         |true                     |
-------------+-------------------------+-------------------------+----------------
time 6       |still waiting...         |notify on mutex, but     |true            
             |Got one signal on mutex  |WON'T unlock it yet      |
             |but CANNOT continue as   |                         |
             |it's not release yet     |                         |
-------------+-------------------------+-------------------------+----------------
time 7       |Can get lock of mutext   |one loop has finished    |true            
             |as odd released it       |now try to acquire the   |
             |now can continue         |mutex again, but cannot  |
             |                         |since it just got locked |
             |                         |by even thread           |
-------------+-------------------------+-------------------------+----------------
time 8       |set the lock state to    |still waiting...         |false           
             |false                    |                         |
-------------+-------------------------+-------------------------+----------------
time 9       |notify on mutex, but     |still waiting...         |false           
             |WON'T unlock it yet      |Got one signal on mutex  |
             |                         |but CANNOT continue as   |
             |                         |it's not release yet     |
-------------+-------------------------+-------------------------+----------------
time 10      |one loop has finished    |Can get lock of mutext   |false           
             |now try to acquire the   |as even released it      |
             |mutex again, but cannot  |now can continue         |
             |since it just got locked |                         |
             |by odd thread            |                         |
-------------+-------------------------+-------------------------+----------------
time 11      |still waiting...         |prints (next odd 3)      |false
             |to acquire lock, not a   |                         |
             |signal                   |                         |
-------------+-------------------------+-------------------------+----------------
time 12      |still waiting...         |won't wait for a signal  |false
             |                         |since lock is false      |
-------------+-------------------------+-------------------------+----------------
....

请注意time 3time 11,这是因为1,然后是3。由于even线程没有机会打印2

现在想,odd 线程先于even 启动?

提示:如果线程B 必须在线程A 之后启动,稳健的方法是从线程A 启动线程B

【讨论】:

    猜你喜欢
    • 2015-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    • 1970-01-01
    • 1970-01-01
    • 2014-04-15
    相关资源
    最近更新 更多