简体   繁体   English

使偶数和奇数线程在Java中以自然顺序打印数字

[英]Make even and odd threads to print numbers in natural order in Java

I know this question has been asked before, But I am unable to figure out why my solution is not working for me. 我知道之前已经问过这个问题,但我无法弄清楚为什么我的解决方案对我不起作用。 I have two threads even and odd, one prints even numbers and other prints odd numbers. 我有两个线程偶数和奇数,一个打印偶数和其他打印奇数。 When I start the threads I want the output to be in natural order of numbers like 0 1 2 3..etc. 当我启动线程时,我希望输出是自然的数字顺序,如0 1 2 3 ..等。 This is my code:- [updated] 这是我的代码: - [更新]

public class ThreadCommunication {


public static void main(String... args) throws InterruptedException
{
    final ThreadCommunication obj = new ThreadCommunication();
    Thread even = new Thread(){
        @Override
        public void run()
        {
            for(int i=0;i<10;i=i+2){
                synchronized(obj){
                    System.out.println(i);
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    };
    Thread odd = new Thread(){
        @Override
        public void run()
        {
            for(int i=1;i<10;i=i+2){
                synchronized(obj){
                    System.out.println(i);
                    obj.notify();
                }
            }

        }
    };
    even.start();
    odd.start();
}

} }

when I run the above code, sometimes it prints the numbers in natural order as expected but sometimes it prints in some other order for ex: 当我运行上面的代码时,有时会按照预期自然顺序打印数字,但有时它会以其他顺序打印出来:

0
1
3
5
7
9
2

What am I doing wrong here? 我在这做错了什么?

Edit: 编辑:

volatile static boolean isAlreadyWaiting = false;



Thread even = new Thread() {
            @Override
            public void run() {
                synchronized (obj) {
                    for (int i = 0; i < 10; i = i + 2) {
                        System.out.println(i);
                        try {
                            if (!isAlreadyWaiting) {
                                isAlreadyWaiting = true;
                                obj.wait();
                            }
                            obj.notify();
                            isAlreadyWaiting=false;

                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        };
        Thread odd = new Thread() {
            @Override
            public void run() {
                synchronized (obj) {
                    for (int i = 1; i < 10; i = i + 2) {
                        System.out.println(i);
                        try {
                            if(isAlreadyWaiting){
                            obj.notify();
                            isAlreadyWaiting = false;
                            }
                            if (!isAlreadyWaiting) {
                                isAlreadyWaiting = true;
                                obj.wait();
                            }

                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        };

Check documentation 检查文档

public class IllegalMonitorStateException extends RuntimeException 公共类IllegalMonitorStateException扩展RuntimeException

Thrown to indicate that a thread has attempted to wait on an object's monitor or to notify other threads waiting on an object's monitor without owning the specified monitor. 抛出此异常表示线程已尝试在对象的监视器上等待 ,或者在没有指定监视器的情况下通知在对象监视器上等待的其他线程。

Monitor is owned by obj Monitor由obj拥有

So you should call 所以你应该打电话

obj.wait();

and

obj.notify();

For more info on Ownership 有关所有权的更多信息

This methods (wait or notify) should only be called by a thread that is the owner of this object's monitor. 此方法(等待或通知)只应由作为此对象监视器所有者的线程调用。 A thread becomes the owner of the object's monitor in one of three ways: 线程以三种方式之一成为对象监视器的所有者:

  1. By executing a synchronized instance method of that object. 通过执行该对象的同步实例方法。
  2. By executing the body of a synchronized statement that synchronizes on the object. 通过执行在对象上同步的synchronized语句的主体。

    1. For objects of type Class, by executing a synchronized static method of that class. 对于Class类型的对象,通过执行该类的同步静态方法。

Only one thread at a time can own an object's monitor. 一次只有一个线程可以拥有对象的监视器。

@Pragnani Kinnera is right about the exception you're seeing. @Pragnani Kinnera对你看到的例外是对的。 But if you want to alternate between even and odd , you'll need to move your second synchronized block into the loop. 但是如果你想在evenodd之间交替,你需要将第二个同步块移动到循环中。 Otherwise, the notifying thread will hold the lock exclusively until the loop completes. 否则,通知线程将保持锁定,直到循环完成。 (As opposed to the first thread, which yields its lock on each round.) (与第一个线程相反,第一个线程在每一轮产生锁定。)

Thread odd = new Thread(){
    @Override
    public void run()
    {

        for(int i=1;i<10;i=i+2){
            synchronized(obj){
                System.out.println(i);
                notify();
            }
        }
    }
};

The first thread, however, should have the loop inside the synchronized block. 但是,第一个线程应该在synchronized块内部有循环。 If both threads release the lock, they both have an equal chance at reacquiring it. 如果两个线程都释放锁定,则它们都有相同的机会重新获取它。 But if the first loop is inside the synchronized block, the second thread won't be able to reenter until the first has completed a full round and is waiting once again. 但是如果第一个循环在同步块内部,则第二个线程将无法重新进入,直到第一个循环完成整轮并再次等待。

EDIT: This still won't work correctly, because there is no guarantee that the first thread won't reacquire the lock before the second thread does, per this quote from the documentation : 编辑:这仍然无法正常工作,因为无法保证第一个线程在第二个线程执行之前不会重新获取锁定,根据文档中的引用:

The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; 唤醒的线程将以通常的方式与可能主动竞争同步此对象的任何其他线程竞争; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object. 例如,唤醒线程在下一个锁定此对象的线程中没有可靠的特权或劣势。

You'll probably want to wake and notify from both threads to ensure they're in sync. 你可能想要从两个线程唤醒并通知它们以确保它们是同步的。

Here is your solution: 这是你的解决方案:

public class ThreadCommunication {


    public static void main(String args[]) throws InterruptedException
    {
        final ThreadCommunication obj = new ThreadCommunication();
        Thread even = new Thread("Even Thread"){
            @Override
            public void run()
            {

                for(int i=0;i<10;i=i+2){
                    System.out.println(i);

                    synchronized(obj){

                        obj.notify();
                    }
                    synchronized(obj){
                        try {
                            obj.wait();
                        }
                        catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }

        };
        Thread odd = new Thread(){
            @Override
            public void run()
            {

                for(int i=1;i<10;i=i+2){
                    try {
                        synchronized(obj){
                            obj.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(i);
                    synchronized(obj){
                        obj.notifyAll();
                    }
                }
            }
        };
        even.start();
        odd.start();
    }

}

As explained by @shmosel, your synchronized block should only contain code that need to be synchronized. 正如@shmosel所解释的,您的synchronized块应该只包含需要同步的代码。

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

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