Java线程同步产生奇怪的输出。

huangapple go评论70阅读模式
英文:

Java thread synchronization gives weird output

问题

我旨在通过一个线程打印奇数,另一个线程打印偶数。下面的实现显示出奇怪的输出。请告诉我在这段代码中我漏掉了什么。

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

// 期望输出  - > 012345678910

// 这段代码的实际输出  - > 013254769810 (流程在变化)

public class OddEvenNumberPrint {
    public static boolean state = false; // 根据这个标志我会等待或通知
    public static Object lock = new 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
英文:

My aim is to print odd numbers by one thread and even numbers by another thread.
Below implementation shows weird output. Please let me know what I am missing in this code.

It prints 0 and 1 as expected. Then it prints 3 and 2.

// Expected - > 012345678910

// Actual output by this code - > 013254769810 (Flow is changing)

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(&quot;Even&quot;);
t1.start();
OddThread t2 = new OddThread(&quot;Odd&quot;);
t2.start();
}
}
class EvenThread extends Thread {
public EvenThread(String name) {
this.setName(name);
}
public void run() {
for (int i = 0; i &lt;= 10; i = i + 2) {
synchronized (OddEvenNumberPrint.lock) {
System.out.println(&quot;Even -&gt;&quot;+i);
while (!OddEvenNumberPrint.state) {
try {
System.out.println(Thread.currentThread().getName()+ &quot; : &quot;+ &quot;Going wait&quot;);
OddEvenNumberPrint.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
OddEvenNumberPrint.state = false;
OddEvenNumberPrint.lock.notify();
System.out.println(Thread.currentThread().getName()+ &quot; : &quot;+ &quot;Released&quot;);
}
}
}
}
class OddThread extends Thread {
public OddThread(String name) {
this.setName(name);
}
public void run() {
for (int i = 1; i &lt; 10; i = i + 2) {
synchronized (OddEvenNumberPrint.lock) {
System.out.println(&quot;Odd -&gt; &quot;+i);
while (OddEvenNumberPrint.state) {
try {
System.out.println(Thread.currentThread().getName()+ &quot; : &quot;+ &quot;Going wait&quot;);
OddEvenNumberPrint.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
OddEvenNumberPrint.state = true;
OddEvenNumberPrint.lock.notify();
System.out.println(Thread.currentThread().getName()+ &quot; : &quot;+ &quot;Released&quot;);
}
}
}
}

Output:

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

答案1

得分: 1

抱歉,我注意到您的要求是只返回翻译的部分,但是您的输入内容实际上已经相当详细。如果您有其他要求或者需要进一步的帮助,请随时提问。

英文:

Considering:

  1. Because even thread is created and started before odd thread, so it doesn't mean even surely will acquire lock of mutex(lock) first, NO! odd has its chance to acquire it before even too. So possible to have 1 and then 0
  2. State management is the key in multi-threading, where your problem sits on it. Signaling must be done at right time/point.

The Problem

The problem with your code is signaling and state-management at wrong points, specially the start point for both threads look buggy.

Let's simulate the run:
Assuming even thread get started and acquire that lock first.
time 0. even thread prints 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&#39;s unlocked from    |
|becasue lock == false    |even                     |
-------------+-------------------------+-------------------------+----------------
time 3       |still waiting...         |prints (next odd 1,...)  |false
-------------+-------------------------+-------------------------+----------------
time 4       |still waiting...         |won&#39;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&#39;T unlock it yet      |
|but CANNOT continue as   |                         |
|it&#39;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&#39;T unlock it yet      |Got one signal on mutex  |
|                         |but CANNOT continue as   |
|                         |it&#39;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&#39;t wait for a signal  |false
|                         |since lock is false      |
-------------+-------------------------+-------------------------+----------------
....

Please mind time 3, and time 11, that's because 1, then 3. Since even thread has no chance to print 2.

Now thinking, that odd thread get started first before even?

Hint: If thread B must be started essentially after thread A, the robust way is starting the thread B from thread A

huangapple
  • 本文由 发表于 2020年9月11日 20:21:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/63847008.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定