为什么我的notify()不起作用?

时间:2013-04-20 17:56:44

标签: java multithreading wait synchronized notify

我的notifyAll()方法似乎都没有工作。 露西假设要等到鲍勃到达然后释放。 Bob假设等待Lucy的确认,然后释放。 这些事情似乎都没有发生。

有人能让我知道我做错了什么以及如何解决这个问题。 提前谢谢。

编辑 - 我使用Grays建议修改了我的代码。 异常消失,但notify()方法似乎仍然无效。

import java.util.logging.Level;
import java.util.logging.Logger;

public class PlayDates {
    Thread lucyThread;
    Girl lucy;
    Thread bobThread;
    Boy bob;

    public static void main(String[] args) {
        PlayDates playDates = new PlayDates();
        playDates.run();
    }
    public void run() {
        lucy = new Girl();
        lucyThread = new Thread(lucy);

        bob = new Boy();
        bobThread = new Thread(bob);

        lucyThread.start();
        threadSleep(500);
        bobThread.start();
    }

    public class Girl implements Runnable {
        @Override
        public void run() {
            synchronized(PlayDates.this){
                System.out.println("Girl synchronized hit");
                if(!bob.hasArrived()) {     // Doesnt seem to get past here?
                    System.out.println("Lucy has fallen asleep waiting for Bob");
                    try {
                        PlayDates.this.wait();  // Wait for Bob
                        System.out.println("Lucy has woken up");
                        PlayDates.this.notifyAll();     // Acknowledge Bobs arrival
                    } catch (InterruptedException ex) {
                        Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
    }

    public class Boy implements Runnable {
        private boolean hasArrived;

        @Override
        public void run() {
            synchronized(PlayDates.this){
                System.out.println("Bob has arrived to play");
                PlayDates.this.notifyAll();
                try {
                    PlayDates.this.wait();  // Wait for Lucy to acknowledge Bobs arrival
                } catch (InterruptedException ex) {
                    Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
                }

                System.out.println("Bob and Lucy are playing");
            }
        }

        public Boy() {
            hasArrived = true;
        }

        public boolean hasArrived() {
            return hasArrived;
        }
    }

    public void threadSleep(int milli) {
        try {
            Thread.sleep(milli);
        } catch (InterruptedException ex) {
            Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

当前输出

Girl synchronized hit
Bob has arrived to play

编辑2 我改编了我的代码,正如格雷斯的建议所暗示的那样。 hasArrived现在是volatile并且在playDates中运行方法。 它在内部类Boys run方法中变为true。 输出没有改变,问题看起来是一样的。 还有什么进一步的建议吗?

更新的代码:

import java.util.logging.Level;
import java.util.logging.Logger;

public class PlayDates {
    Thread lucyThread;
    Girl lucy;
    Thread bobThread;
    Boy bob;
    volatile boolean hasArrived;

    public static void main(String[] args) {
        PlayDates playDates = new PlayDates();
        playDates.run();
    }
    public void run() {
        hasArrived = false;
        lucy = new Girl();
        lucyThread = new Thread(lucy);

        bob = new Boy();
        bobThread = new Thread(bob);

        lucyThread.start();
        threadSleep(500);
        bobThread.start();
    }

    public class Girl implements Runnable {
        @Override
        public void run() {
            synchronized(PlayDates.this){
                System.out.println("Girl synchronized hit");
                if(hasArrived) {     // Doesnt seem to get past here?
                    System.out.println("Lucy has fallen asleep waiting for Bob");
                    try {
                        PlayDates.this.wait();  // Wait for Bob
                        System.out.println("Lucy has woken up");
                        PlayDates.this.notifyAll();     // Acknowledge Bobs arrival
                    } catch (InterruptedException ex) {
                        Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
    }

    public class Boy implements Runnable {
        @Override
        public void run() {
            threadSleep(1000);
            synchronized(PlayDates.this){
                System.out.println("Bob has arrived to play");
                hasArrived = true;
                PlayDates.this.notifyAll();
                try {
                    PlayDates.this.wait();  // Wait for Lucy to acknowledge Bobs arrival
                } catch (InterruptedException ex) {
                    Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
                }
                System.out.println("Bob and Lucy are playing");
            }
        }
    }

    public void threadSleep(int milli) {
        try {
            Thread.sleep(milli);
        } catch (InterruptedException ex) {
            Logger.getLogger(PlayDates.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

1 个答案:

答案 0 :(得分:3)

在初始化Girl字段之前,您正在启动bob主题,以便获得NPE。您应该在 bob之前初始化Girl字段,并将其传递到该主题。您的程序可能在某些情况下有效,但存在无法预测的竞争条件。如果线程启动得足够快,它可能会起作用,但您应该在启动bob线程之前初始化Girl

您还有一些memory synchronization issues。例如,虽然您正在PlayDates.this进行同步,但Boy线程调用Girl时,bob.hasArrived()类可能尚未初始化和同步。每当在多个线程中访问字段时,您需要确保两个线程都看到正确同步的值。您可以将hasArrived设为AtomicBoolean或将hasArrived标记为volatile来确保这一点。

修改

问题在于改变,所以我会努力跟上。我建议 not hasArrived设置为Boy中的volatile。我认为您应该将其设为run()并在Girl方法中设置。您希望Boy线程启动,运行一点,然后看到wait()不可用且Boy。因此,hasArrived线程应该稍后开始,并在true后的run()方法中将sleep()设置为{{1}}。