开发者

Thread Deadlock

开发者 https://www.devze.com 2022-12-20 06:32 出处:网络
I have 2 threads. One thread prints odd numbers and the second thread prints even numbers. Now, I have to execute the threads alternatively so that i canoutput 1,2,3,4,5,6,.....

I have 2 threads. One thread prints odd numbers and the second thread prints even numbers. Now, I have to execute the threads alternatively so that i can output 1,2,3,4,5,6,.....

I have written a program for this and this is resulting in a deadlock. Can someone explain what is the problem with the code and how to rectify it?

class BooleanObject {
boolean flag;
BooleanObject(boolean flag) {
    this.flag = flag;
}
}
class E开发者_Go百科venThread extends Thread {
Object lock;
BooleanObject flagObj;
EvenThread(Object o, BooleanObject flag) {
    lock = o;
    this.flagObj = flag;
}
public void run() {
    for (int i=2;i<100;i+=2) {
        synchronized(lock) {
            if (flagObj.flag == false) {
                flagObj.flag = true;
                lock.notify();
            }
            else {
                try {
                    while (flagObj.flag == true) {
                        lock.wait();
                    }
                }
                catch (InterruptedException e) {

                }
            }
            System.out.println(i);
        }
    }
}
}

class OddThread extends Thread {
Object lock;
BooleanObject flagObj;
OddThread(Object o, BooleanObject flag) {
    lock = o;
    this.flagObj = flag;
}
public void run() {
    for (int i=1;i<100;i+=2) {
        synchronized(lock) {
            if (flagObj.flag == true) {
                flagObj.flag = false;
                lock.notify();
            }

            else {
                try {
                    while(flagObj.flag == false) {
                        lock.wait();
                    }
                }
                catch (InterruptedException e) {

                }
            }
            System.out.println(i);
        }
    }
}
}

public class EvenOddThreads {
public static void main(String[] args) {
    Object obj = new Object();
    BooleanObject flagObj = new BooleanObject(true);
    EvenThread et = new EvenThread(obj,flagObj);
    OddThread ot = new OddThread(obj,flagObj);

    et.setName("even thread");
    ot.setName("odd thread");

    et.start();
    ot.start();
}
}


The problem is with auto-boxing. When you change flag from true to false or vice versa, you are actually getting an entirely new Boolean object. That is, this line:

flag = false;

Is equivalent to:

flag = new Boolean(false);

Once that happens your two threads are then referring to two different Boolean objects, so their flags end up un-synchronized and neither thread is able to signal the other to wake up. When OddThread changes the flag EvenThread still has the old flag object so it doesn't see the new value.

Because a Boolean object is immutable you'll need to change your flag to use some other mutable object which can change values in place without creating new objects. That, or have both classes refer to a common (perhaps global) variable.

As @erickson suggests you could use AtomicBoolean which is mutable. Another kludgy way to do it would be to change flag to:

boolean[] flag = new boolean[1];

And then use flag[0] every where. Both threads would then be able to change flag[0] while always referencing the same boolean[] array object. You wouldn't have the auto-boxing problem.

...

Also, it is a good idea to wrap any call to wait() in a loop. A wait() can be subject to spurious wakeups where the call returns even though nobody has actually called notify(). To workaround that you should always check your guarding condition after waking up to make sure the wakeup isn't spurious.

while (flag == true) {
    lock.wait();
}

Update

I have made the changes based on your suggestions above; but i am not getting the expected output. I will paste the modified code above. Here is the output i am getting 1 2 4 3 5 6 8 7 9 10 11 13 12 15 17 14....

When you end up waiting, once you are woken up you don't toggle flag and notify the other thread. I advise reorganizing your code a bit so it looks like "wait; print; notify". Something like:

synchronized (lock) {
    while (flagObj.flag == false) {
        lock.wait();
    }

    System.out.println(i);

    flagObj.flag = false;
    lock.notify();
}


The problem isn't autoboxing. The same thing would happen even if boolean primitives were used throughout.

It's a scope problem. Each thread instance has its own flag member, and they are completely distinct. When you assign a new value in one thread, the other thread cannot see it.

To make this work as intended, make a mutable boolean wrapper (an AtomicBoolean would do the job, although you wouldn't be making use of its concurrency properties in this application), and pass that wrapper to each thread. Each thread would mutate that single object, rather than assigning a new object to its own variable.


You actually have two problems here.

1) The first one is this

if (flag == true) {
    flag = false;
    lock.notify();
}

You pass the flag reference to the constructor, but then you change each thread's local flag, which won't affect the other thread's value.

Try something like

class Monitor
{
    public static volatile boolean flag;
}

And then use Monitor.flag in each thread.

2) The second problem (once the 1st one is fixed), is that each thread needs to have this

synchronized(lock)
{
    lock.notify();
}

at the end after the loop because otherwise one thread will wait() but the other thread is already done.

0

精彩评论

暂无评论...
验证码 换一张
取 消