我确信我的问题有多个答案。但是,我正在学习多线程的基本概念,并且我想出了下面的代码。
有两个线程:一个打印偶数,另一个打印奇数。由于某种原因,他们首先都打印出正确的数字,然后“交换”角色。而且,他们似乎不仅仅打印前 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 的数字。两个线程应该共享一个对象,因此我想出了一个共享对象。因此,由于同一个对象在 2 个线程之间共享,因此同步块应该能够正确更新值。
您的实现在设计、泛化和一般逻辑方面存在一些问题。
您声明了两个类,它们基本上执行完全相同的操作:打印数字。唯一不同的是条件:打印的数字必须是奇数还是偶数。您已经可以使用一个
Thread
类来实现这一目标,其中唯一需要参数化的是打印条件。 printOdd()
和 printEven()
方法几乎是复制/粘贴。
此外,班级职责也没有处理好。您的
SharedObject
类基本上是一个计数器,它不仅跟踪并递增计数值,而且还必须处理两个线程的逻辑,这是不应该落在它上面的。它的唯一目标应该是通过并行执行以一致的方式增加共享值。您应该在您的 Thread
中重定向该打印逻辑,因为如您所见,它们的代码基本上只包含进行一次调用。
您的
printOdd
和 printEven()
方法中的逻辑存在一些漏洞。两个线程都设法在开始时只打印一次相应的数字类型(System.out.println
,没有任何文本)。这是因为:
if (counter % 2 != 0) {
System.out.println(counter);
}
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!");
}
if (counter % 2 == 0) {
System.out.println(counter);
}
counter++;
System.out.println("EvenThread: Notify: Counter: " + counter);
notify();
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();
}
}