我敢肯定我的問題有多個答案。但是,我正在學習多執行緒的基本概念,我想出了下面的代碼。
有兩個執行緒:一個列印偶數,另一個列印奇數。出于某種原因,他們都首先列印了正確的號碼,然后他們“交換”了角色。此外,它們似乎列印的不僅僅是前 10 個數字。
為什么它沒有給出正確的輸出?
package com.thread;
public class OddEventThread {
public static void main(String[] args) {
SharedResource obj = new SharedResource();
OddThread oddThread = new OddThread(obj);
EvenThread evenThread = new EvenThread(obj);
System.out.println("Starting Odd/Even Thread");
oddThread.start();
evenThread.start();
}
}
class OddThread extends Thread {
SharedResource obj;
public OddThread(SharedResource obj) {
this.obj = obj;
}
@Override
public void run() {
System.out.println("OddThread");
obj.printOdd();
}
}
class EvenThread extends Thread {
SharedResource obj;
public EvenThread(SharedResource obj) {
this.obj = obj;
}
@Override
public void run() {
System.out.println("EvenThread");
obj.printEven();
}
}
class SharedResource {
private int N = 10;
private int counter = 1;
public void printOdd() {
System.out.println("printOdd");
synchronized (this) {
System.out.println("OddThread: Counter: " counter);
while (counter <= N) {
if (counter % 2 != 0) {
System.out.println(counter);
} else {
try {
System.out.println("OddThread: Wait: Counter: " counter);
wait();
} catch (InterruptedException e) {
System.out.println("Interrupted Exception!");
}
}
counter ;
System.out.println("OddThread: Notify: Counter: " counter);
notify();
}
}
}
public void printEven() {
System.out.println("printEven");
synchronized (this) {
System.out.println("EvenThread: Counter: " counter);
while (counter <= N) {
if (counter % 2 == 0) {
System.out.println(counter);
} else {
try {
System.out.println("EvenThread: Wait: Counter: " counter);
wait();
} catch (InterruptedException e) {
System.out.println("Interrupted Exception!");
}
}
counter ;
System.out.println("EvenThread: Notify: Counter: " counter);
notify();
}
}
}
}
輸出:
Starting Odd/Even Thread
OddThread
printOdd
EvenThread
printEven
OddThread: Counter: 1
1
OddThread: Notify: Counter: 2
OddThread: Wait: Counter: 2
EvenThread: Counter: 2
2
EvenThread: Notify: Counter: 3
EvenThread: Wait: Counter: 3
OddThread: Notify: Counter: 4
OddThread: Wait: Counter: 4
EvenThread: Notify: Counter: 5
EvenThread: Wait: Counter: 5
OddThread: Notify: Counter: 6
OddThread: Wait: Counter: 6
EvenThread: Notify: Counter: 7
EvenThread: Wait: Counter: 7
OddThread: Notify: Counter: 8
OddThread: Wait: Counter: 8
EvenThread: Notify: Counter: 9
EvenThread: Wait: Counter: 9
OddThread: Notify: Counter: 10
OddThread: Wait: Counter: 10
EvenThread: Notify: Counter: 11
OddThread: Notify: Counter: 12
這是我提出這個解決方案的思考程序:
我們有 2 個執行緒列印從 1 到 10 的數字。兩個執行緒應該共享一個物件,因此我想出了一個 sharedObj。因此,由于同一個物件在 2 個執行緒之間共享,因此同步塊應該可以正確更新值。
uj5u.com熱心網友回復:
您的實作中存在一些關于其設計、概括和一般邏輯的問題。
你宣告了兩個基本上做同樣事情的類:列印數字。唯一不同的是條件:列印的數字必須是奇數還是偶數。您已經可以通過一個Thread
類來實作這一點,其中唯一需要引數化的是列印條件。和方法幾乎是復制/粘貼printOdd()
。printEven()
此外,班級責任沒有得到很好的處理。你的SharedObject
類基本上是一個計數器,它不僅跟蹤和增加計數值,而且還必須處理兩個執行緒的邏輯,這是不應該落在它上面的東西。它的唯一目標應該是以與并行執行一致的方式增加共享值。您應該在您的 中重定向該列印邏輯Thread
,因為如您所見,他們的代碼基本上包括進行一次呼叫。
回答
printOdd
最后,你的和方法中的邏輯printEven()
有一些漏洞。兩個執行緒都設法在開始時僅列印一次相應的數字型別(System.out.println
沒有任何文本)。這是因為:
- 其中一個執行緒獲取了 SharedObject 的監視器,比如說 OddThread,這意味著 EvenThread 等待 OddThread 釋放鎖以進入同步塊。此時,OddThread 列印出其對應的數字 (1):
if (counter % 2 != 0) {
System.out.println(counter);
}
- 然后,OddThread 將值增加到 2,列印通知陳述句并最終通知另一個執行緒(請記住,當執行緒執行所有這些操作時,它仍然擁有 SharedObject 的監視器)。
counter ;
System.out.println("OddThread: Notify: Counter: " counter);
notify();
- 然后,回圈結束并
while
測驗條件。由于數字現在是偶數,因此跟蹤if
失敗,OddThread 釋放鎖并等待,直到它得到 EvenThread 的通知。
try {
System.out.println("OddThread: Wait: Counter: " counter);
wait();
} catch (InterruptedException e) {
System.out.println("Interrupted Exception!");
}
- 然后,EvenThread 終于可以進入同步陳述句并列印它的值,即 2。
if (counter % 2 == 0) {
System.out.println(counter);
}
- 然后,它將值從 2 增加到 3,用值 3 列印 notify 陳述句(因此它列印兩次,第二次列印奇數,即使它是偶數執行緒),最后通知另一個執行緒。
counter ;
System.out.println("EvenThread: Notify: Counter: " counter);
notify();
- 然后,EvenThread 測驗
while
條件,使if
陳述句失敗,列印等待陳述句,然后等待 OddThread 喚醒它。
try {
System.out.println("EvenThread: Wait: Counter: " counter);
wait();
} catch (InterruptedException e) {
System.out.println("Interrupted Exception!");
}
- 從現在開始,每個執行緒將從他們的最后一次
wait()
呼叫繼續。當數字是正確的“型別”時,它們都會恢復,但隨后它們會增加其值,使其成為相反的型別,然后列印通知陳述句。這不僅解釋了為什么每個執行緒都列印它們相反的數字型別,而且還解釋了為什么它們即使在達到最大值 10 后仍繼續列印。在到達最后一次迭代后,它們都增加了一次,while
因為它們都從上次wait()
呼叫中恢復。
解決方案
這是一個修復了所有設計、泛化和邏輯漏洞的實作。
class Main {
public static void main(String[] args) {
SharedCounter counter = new SharedCounter();
ThreadPrintingNums oddThread = new ThreadPrintingNums("OddThread", counter, false, 10);
ThreadPrintingNums evenThread = new ThreadPrintingNums("EvenThread",counter, true, 10);
System.out.println("Starting Threads");
oddThread.start();
evenThread.start();
}
}
class ThreadPrintingNums extends Thread {
private SharedCounter counter;
private boolean flagPrintEven;
private int max;
public ThreadPrintingNums(String threadName, SharedCounter obj, boolean flagPrintEven, int max) {
setName(threadName);
this.counter = obj;
this.flagPrintEven = flagPrintEven;
this.max = max;
}
@Override
public void run() {
while (counter.getCounter() <= max) {
if (counter.getCounter() % 2 == (flagPrintEven ? 0 : 1)) {
System.out.printf("%s => %d%n", getName(), counter.getCounter());
counter.incCounter();
} else {
try {
synchronized (counter) {
counter.wait();
}
} catch (InterruptedException e) {
System.out.printf("%s interrupted exception", getName());
System.exit(-1);
}
}
}
}
}
class SharedCounter {
private int counter;
public SharedCounter() {
this.counter = 1;
}
public synchronized int getCounter() {
return counter;
}
public synchronized void incCounter() {
counter ;
notify();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/470133.html
上一篇:異步未來執行緒執行器