繁体   English   中英

使用两个线程打印偶数奇数

[英]Print Even Odd number using two threads

我试图通过两个线程重复使用wait和notify来打印偶数和奇数。 但是,我已经浏览了网站中给出的所有实现。 尽管我是多线程开发人员的第一次尝试,但是我无法获得理想的结果。 在这里,我在下面粘贴我的代码:请您检查一下我犯错的地方,并进行更正和解释,并进行复述。

package com.test.printEvenOdd;

public class PrintOddEvenNumbers {

    public static void main(String[] args){

        String s = new String("");

        EvenThread t1= new EvenThread(s);
        OddThread t2= new OddThread(s);
        Thread th1 = new Thread(t1);
        Thread th2 = new Thread(t2);
        th1.start();
        th2.start();
    }

}

class EvenThread implements Runnable{
    String s;
    EvenThread(String s){
        this.s= s;
    }

    @Override
    public void run() {
        synchronized(s){
            for(int i=1;i<=10;i++){

                if(i%2==0){
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(i);
                    s.notify();
                }
                try {
                    s.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }
    }

}

class OddThread implements Runnable{

    String s;
    OddThread(String s){
        this.s= s;
    }

    @Override
    public void run() {
        synchronized(s){
            for(int i=1;i<=10;i++){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if(i%2==1){
                    System.out.println(i);
                    s.notify();
                }
                try {
                    s.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

}

您的问题是您的锁定 过于保守/严格:

您将锁放在整个循环中; 对于两个线程。

因此,一个线程进入了它的循环。 但很快就无法进行。 因为它需要其他线程才能进行。 但是第二个线程甚至无法启动-因为它完全可以进入其循环!

换句话说:为了进步; 两个线程都必须能够进入各自的循环; 并取得足够的进展,以便其他线程可以执行下一步。

这就像建立一个只有两个人可以一起离开的房间; 但是您只允许一个人进入那个房间。

欢迎使用多线程编程; 您刚刚创建了第一个死锁

并记录在案:重新布置锁时; 确保正确地发出信号; 这样等待/通知可以按预期工作。

最后:如果您仔细看一下代码; 您会发现您重复了很多代码。 那总是一个坏主意。 相反:尝试找出哪些部分确实不同; 其他...在源代码中应该只存在一次 因此,作为另一项练习:当您重新排列代码以使其能够执行预期的工作时-请尝试是否可以对其进行重构,以使代码重复量最小化。 我向您保证,这是值得您花时间的练习!

您应该将“ wait()”移至“ if”块内。 其他线程将进入等待状态,而不会通知其他正在等待的线程,并且两个线程都将处于等待状态。

        if(i%2==0){
            synchronized(s){
                System.out.println(i);
                try {
                    s.notify();
                    s.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

代码有问题。 无需睡觉。 如先前的答复中所述,您过于急切地进行了同步,这是不必要的。 无法保证偶数线程将首先启动还是奇数线程将首先启动。 它取决于哪个线程首先要获取锁。 最后,一个线程将永远等待,因为另一个线程将已经出来,此后没有人通知。 并且任何wait()代码都应处理此处解释的虚假唤醒

您的初始代码有很多问题。 有关它们的说明,请参见GhostCat的答案。 通常,这种计算不适用于多线程,因为(显然)您希望顺序打印数字。 但是,鉴于这一愿望并希望使用2个线程交织来做到这一点,您可以按照以下方式进行操作。 请注意,此解决方案仍然存在一些问题。 该线程依赖于已执行的另一个线程能够达到其自身的结束条件,这意味着,如果仅为奇数(或偶数)创建一个,则将陷入无限循环。

import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.IntPredicate;

public class Foo {

    public static void main(String[] args) {
        // an executor service will handle the thread pool and scheduling
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(new NumberPrintAndIncrement(i -> i % 2 != 0));
        pool.submit(new NumberPrintAndIncrement(i -> i % 2 == 0));
        // you want to shut down the pool when the threads are done
        pool.shutdown();
    }
}

final class NumberPrintAndIncrement implements Runnable {

    // Need a shared lock for accessing and updating the current number
    private static final Object LOCK = new Object();
    // The number is shared between threads so it needs to be volatile
    private static volatile int number = 1;

    // Instance variable for letting a particular runnable know if it should
    // print the number in it's current state
    private final IntPredicate predicate;

    NumberPrintAndIncrement(IntPredicate predicate) {
        this.predicate = Objects.requireNonNull(predicate);
    }

    @Override
    public void run() {
        while (number < 10) {
            // this could run at any point and any number of times, but
            // that doesn't matter since it is just doing a quick check and
            // a possible update. If the number doesn't satisfy the predicate,
            // this will just be a no-op. Having a predicate means
            // you don't have to rely on wait and notify to try and
            // achieve interleaving the number output properly which
            // is good due to the liveness problem Rajesh mentioned.
            synchronized (LOCK) {
                if (predicate.test(number)) {
                    System.out.println(number);
                    number++;
                }
            }
        }
    }
}

为了更好地了解正在发生的事情,让我们看一下每个线程中发生的步骤。

public class PrintOddEvenNumbers {

    public static void main(String[] args){

        String s = new String("");

        EvenThread t1= new EvenThread(s);
        OddThread t2= new OddThread(s);
        Thread th1 = new Thread(t1);
        Thread th2 = new Thread(t2);
        th1.start();
        th2.start();
    }

}

class EvenThread implements Runnable{
    String s;
    EvenThread(String s){
        this.s= s;
    }

    @Override
    public void run() {
        synchronized(s){
            for(int i=1;i<=10;i++){
                System.out.println("EvenThread i: " + i);

                if(i%2==0){
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(i);
                    System.out.println("EvenThread notify");
                    s.notify();
                }
                try {
                    System.out.println("EvenThread waiting..");
                    s.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }
    }
}

class OddThread implements Runnable{

    String s;
    OddThread(String s){
        this.s= s;
    }

    @Override
    public void run() {
        synchronized(s){
            for(int i=1;i<=10;i++){
                System.out.println("OddThread i: " + i);

                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if(i%2==1){
                    System.out.println(i);
                    System.out.println("OddThread notify");
                    s.notify();
                }
                try {
                    System.out.println("OddThread waiting..");
                    s.wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

这将打印:

EvenThread i: 1
EvenThread waiting..
OddThread i: 1
1
OddThread notify
OddThread waiting..
EvenThread i: 2
2
EvenThread notify
EvenThread waiting..
OddThread i: 2
OddThread waiting..

一个简单的解释:

  • 当OddThread达到i2时 ,它waits s释放。
  • 当EvenThread达到2的 i时,它也waits s被释放。

现在,您有两个线程都在等待被唤醒( 死锁 )。

发生这种情况的原因是,为了满足使用notify唤醒另一个等待线程的条件,即i%2==1i%2=0

这不是唯一的问题,但也存在一些基本问题。

  • 在这种情况下,如果在生产中使用Threads的话是不正确的,因为无论如何您都试图进行顺序工作,因此为每个任务创建Threads的开销会增加不必要的开销。
  • 没有共享资源,使synchornize成为冗余。
  • 您期望一个线程比另一个线程先拥有一个锁,这不是Thread的工作原理-它可以是第一个先持有该锁的线程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM