Java线程同步提供了奇怪的输出

问题描述

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

它按预期打印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. 因为even线程是在odd线程之前创建和启动的,所以它不是意味着even肯定会获得互斥锁({{1 }})首先,不! lock也有机会在odd之前获得它。因此可以先拥有even,然后拥有1
  2. 状态管理是多线程的关键,而问题就出在这里。信号必须在正确的时间/点完成。

问题

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

让我们模拟跑步:
假设0线程开始并首先获取该even。 时间0。lock线程输出0

even

请注意 |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 3,这是因为1,然后是3。由于time 11线程没有机会打印even

现在正在考虑,2线程是在odd之前首先启动的吗?

提示::如果必须在线程even之后实质上启动线程B,则可靠的方法是从线程A启动线程B