简体   繁体   中英

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. 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

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

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.

    1. For objects of type Class, by executing a synchronized static method of that class.

Only one thread at a time can own an object's monitor.

@Pragnani Kinnera is right about the exception you're seeing. But if you want to alternate between even and odd , you'll need to move your second synchronized block into the loop. 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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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