简体   繁体   English

线程死锁

[英]Thread Deadlock

I have 2 threads. 我有2个帖子。 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,..... 现在,我必须交替执行线程,以便我可以输出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 EvenThread 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. 当您将flag从true更改为false或反之,您实际上获得了一个全新的Boolean对象。 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. 一旦发生这种情况,你的两个线程就会引用两个不同的Boolean对象,因此它们的标志最终不同步,并且两个线程都不能发出信号通知另一个线程唤醒。 When OddThread changes the flag EvenThread still has the old flag object so it doesn't see the new value. OddThread更改标志时, EvenThread仍然具有旧标志对象,因此它不会看到新值。

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. 因为Boolean对象是不可变的,所以您需要更改标志以使用其他可变对象,这些对象可以在不创建新对象的情况下更改值。 That, or have both classes refer to a common (perhaps global) variable. 那,或者两个类都引用一个共同的(也许是全局的)变量。

As @erickson suggests you could use AtomicBoolean which is mutable. 正如@erickson建议你可以使用可变的AtomicBoolean Another kludgy way to do it would be to change flag to: 另一种做法的方法是将flag改为:

boolean[] flag = new boolean[1];

And then use flag[0] every where. 然后在每个地方使用flag[0] Both threads would then be able to change flag[0] while always referencing the same boolean[] array object. 然后,两个线程都能够更改flag[0]同时始终引用相同的boolean[]数组对象。 You wouldn't have the auto-boxing problem. 你不会有自动拳击问题。

... ...

Also, it is a good idea to wrap any call to wait() in a loop. 此外,最好在循环中包装对wait()任何调用。 A wait() can be subject to spurious wakeups where the call returns even though nobody has actually called notify() . 即使没有人实际调用notify() wait()也可能会受到虚假唤醒的影响。 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.... 这是我得到的输出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. 当你最后等待时,一旦你被唤醒,你不要切换flag并通知另一个线程。 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. 即使在整个过程中使用了boolean元,也会发生同样的事情。

It's a scope problem. 这是一个范围问题。 Each thread instance has its own flag member, and they are completely distinct. 每个线程实例都有自己的flag成员,它们完全不同。 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. 为了使这个工作按预期工作,创建一个可变的 boolean包装器( AtomicBoolean将完成这项工作,尽管你不会在这个应用程序中使用它的并发属性),并将该包装器传递给每个线程。 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 1)第一个就是这个

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. 您将标志引用传递给构造函数,但随后更改每个线程的本地flag ,这不会影响其他线程的值。

Try something like 尝试类似的东西

class Monitor
{
    public static volatile boolean flag;
}

And then use Monitor.flag in each thread. 然后在每个线程中使用Monitor.flag

2) The second problem (once the 1st one is fixed), is that each thread needs to have this 2)第二个问题(一旦第一个被修复),每个线程都需要这个

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

at the end after the loop because otherwise one thread will wait() but the other thread is already done. 在循环结束后,因为否则一个线程将等待()但另一个线程已经完成。

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

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