使用Java中的2个线程打印奇数数字

发布于 2025-01-25 21:15:02 字数 3561 浏览 3 评论 0原文

我确定我的问题有多个答案。但是,我正在学习多线程的基本概念,我想到了以下代码。

有两个线程:一个打印均匀的数字,另一个打印奇数。由于某种原因,他们首先打印出正确的数字,然后“交换”角色。而且,它们似乎不仅打印了前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打印数字。这两个线程都应共享一个对象,因此我想出了一个共享obj。因此,由于在2个线程之间共享相同的对象,因此同步块应在正确更新该值方面起作用。

I'm sure there are multiple answers to my question. But, I am learning the basic concepts of multi-threading and I came up with the code below.

There are two threads: one prints even numbers and the other prints odd numbers. For some reason, they both print their right number at first and then they "swap" role. Also, they seem to print more than just the first 10 numbers.

Why is it not giving the correct output?

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();
            }
        }
    }
}

Output:

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

Here is my thought process of coming up with this solution:

we have 2 threads which print numbers from 1 to 10. Both threads should share an object, hence I came up with one sharedObj. So, since the same object is shared between 2 threads, then the synchronized block should work in properly updating the value.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

紫罗兰の梦幻 2025-02-01 21:15:02

代码问题

您的设计和一般逻辑存在一些问题。

在您的代码中,基本上有两个类别做完全相同的类:打印数字。唯一不同的条件是:印刷数字是否必须奇怪甚至。您已经可以使用一个thread类来实现这一目标,其中唯一要参数化的是打印条件。 printodd()printeven()方法几乎是复制/粘贴。

此外,关注点的分离不能正确处理。您的sharedObject类基本上是一个计数器,它不仅可以保持跟踪和增加计数值,而且还必须处理两个线程的逻辑,这是不应该落在其上的东西。它的唯一目标应该是以一致的方式和并行执行以一致的方式递增共享值。您应该在thread中重定向该打印逻辑,因为如您所见,它们的代码基本上是在拨打单个呼叫中。

在您的代码中分解代码

,两个线程都仅在开始时就打印了正确的数字(system.out.println没有任何文本),因为:

  1. 其中一个线程获取sharedObject < /code>的监视器,假设oddThread,这意味着EventHread等待锁定版本以输入同步块。此时,OddThread打印其相应的数字(1):
if (counter % 2 != 0) {
    System.out.println(counter);
}
  1. oddThread将值递增到2,打印Notify语句,最后通知另一个线程。请记住,当该线程正在执行所有操作时,它仍然拥有sharedObject的监视器。
counter++;
System.out.println("OddThread: Notify: Counter: " + counter);
notify();
  1. 循环结束和条件测试时。以下如果失败,因为现在数字均匀,oddthread发布锁定并等待,直到eventhread通知。
try {
    System.out.println("OddThread: Wait: Counter: " + counter);
    wait();
} catch (InterruptedException e) {
    System.out.println("Interrupted Exception!");
}
  1. EventHread最终可以输入同步语句并打印其值(2)。
if (counter % 2 == 0) {
    System.out.println(counter);
}
  1. EventHread将值递增到3,用值3打印Notify语句(因此它的打印两次,也是错误的数字),最后是通知另一个线程。
counter++;
System.out.println("EvenThread: Notify: Counter: " + counter);
notify();
  1. eventHread测试条件,失败如果语句,打印等待语句,然后等待oddthread唤醒它。
try {
    System.out.println("EvenThread: Wait: Counter: " + counter);
    wait();
} catch (InterruptedException e) {
    System.out.println("Interrupted Exception!");
}
  1. 从现在开始,每个线程将从其最后一个Wait() Invocation 继续。当数字是正确的“类型”时,他们俩都恢复,将值(使其相对类型)增加,然后打印Notify语句。这不仅说明了为什么每个线程都会打印其相对的数字类型,而且还解释了为什么即使达到10最大值10之后,它们也会继续打印。在迭代时,他们都在到达最后一个后又增加了一次,因为他们都从最后一个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();
    }
}

Code Issues

There are some issues in your implementation regarding the design and general logic.

In your code, there are two classes that basically do the exact same thing: printing numbers. The only thing that differs is the condition: whether the printed numbers must be odd or even. You could already achieve this with one Thread class, where the only thing to parameterize is the printing condition. The printOdd() and printEven() methods are almost a copy/paste.

Furthermore, the separation of concerns is not dealt properly. Your SharedObject class is basically a counter, which not only keeps track and increments the counting value, but it has also to deal with both threads' logic, which is something that should not fall on it. Its sole goal should be to increment a shared value in a consistent way with parallel execution. You should redirect that printing logic within your Thread, since, as you can see, their code basically consists in making a single call.

Breaking down your code

In your code, both threads manage to print the right number only at the start (the System.out.println without any text) because:

  1. One of the threads acquires SharedObject's monitor, let's say OddThread, which means that EvenThread awaits the lock release in order to enter the synchronized block. At this point, OddThread prints its corresponding number (1):
if (counter % 2 != 0) {
    System.out.println(counter);
}
  1. OddThread increments the value to 2, prints the notify statement, and finally notifies the other thread. Bear in mind that while the thread is doing all of this, it's still owning the SharedObject's monitor.
counter++;
System.out.println("OddThread: Notify: Counter: " + counter);
notify();
  1. The loop ends and the while condition is tested. The following if fails, as the number is now even, OddThread releases the lock and waits until is notified by EvenThread.
try {
    System.out.println("OddThread: Wait: Counter: " + counter);
    wait();
} catch (InterruptedException e) {
    System.out.println("Interrupted Exception!");
}
  1. EvenThread can finally enter the synchronized statement and prints its value (2).
if (counter % 2 == 0) {
    System.out.println(counter);
}
  1. EvenThread increments the value to 3, prints the notify statement with value 3 (so it's printing twice and also the wrong number), and finally notifies the other thread.
counter++;
System.out.println("EvenThread: Notify: Counter: " + counter);
notify();
  1. EvenThread tests the while condition, fails the if statement, prints the waiting statement, and then awaits for OddThread to awake it.
try {
    System.out.println("EvenThread: Wait: Counter: " + counter);
    wait();
} catch (InterruptedException e) {
    System.out.println("Interrupted Exception!");
}
  1. From now on, each thread will continue from their last wait() invocation. Both of them resume when the number is their right "type", increment the value (making it their opposing type), and then print the notify statement. This, not only explains why each thread prints their opposing number type, but it also explains why they keep printing even after the max value of 10 is reached. They both increment one more time after reaching the last while iteration, since they both resume from their last wait() call.

Solution

Here is an implementation addressing the issues listed above.

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();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文