在if条件中调用Java notify()

时间:2016-09-10 06:01:51

标签: java multithreading wait notify

我刚刚编写了一个简单的java示例来熟悉wait和notify方法的概念。

这个想法是,当调用notify()时,主线程将打印总和。

MyThread类

public class MyThread extends Thread {
    public int times = 0;

    @Override
    public void run() {

        synchronized (this) {
            try {
                for (int i = 0; i < 10; i++) {
                    times += 1;
                    Thread.sleep(500);
                    if (i == 5) {
                        this.notify();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

主要类

public class Main {

    public static void main(String[] args) {

        MyThread t = new MyThread();
        synchronized (t) {
            t.start();
            try {
                t.wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(t.times);
        }
    }
}

预期结果

5但我得了10个。

嗯,我认为当调用notify()时,主线程将被唤醒并执行应该给出的System.out.println(t.times) 5.然后run()将继续直到它完成for循环,它将把时间值更新为10。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:5)

同步块意味着相互排斥。在任何给定时刻,只允许一个线程持有锁并在同步块内执行代码。此规则分布在由同一个锁保护的所有块上。

在你的情况下,有两个这样的块使用相同的锁,因此它是主线程或允许在这些块中的任何一个块中执行代码的MyThread,另一个线程必须等待。因此,您有以下方案:

  1. 主线程获得锁定。
  2. 主线程启动第二个线程。
  3. 第二个线程命中同步块,但由于主线程正在保持锁定,因此无法输入。
  4. 主线程调用wait()。此调用释放锁定并将主线程置于WAITING状态。
  5. 第二个线程现在可以获取锁并进入同步块。
  6. 第二个帖子计为五个并调用notify()。此调用不释放锁定,它只是通知主线程一旦可以重新获取锁定就可以进行。
  7. 主线程唤醒但它无法取得进展,因为它无法重新获取锁(它仍然被第二个线程保持)。请记住,同一个锁中的两个线程不能同时被同一个锁保护,现在,第二个线程仍处于活动状态,因此主线程必须继续等待。
  8. 第二个线程继续计数,将times设置为10并最终离开同步块,释放锁定。
  9. 主线程重新获取锁定,现在可以进入println。但是到了这个时候,times已经是10。
  10. 使用join()也无济于事,因为结果将是相同的 - 主线程只能在第二个完成后才能取得进展。

    如果您希望主线程在第二个线程命中5后继续执行,则需要获取锁定并在该事件发生后立即释放:

    public class MyThread extends Thread {
        public volatile int times = 0;
    
        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    times += 1;
                    Thread.sleep(500);
                    if (i == 5) {
                        synchronized(this) {
                            this.notify();
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    不要忘记使times易变,否则JVM将无法保证您在主线程中看到它的实际值。

    你还应该明白,这种方法并不能保证主线程打印5.可能会发生这种情况,当它到达println调用时,第二个线程会进行一次或两次甚至更多次迭代并且你会看到大于5的东西(尽管由于每次迭代都调用sleep()而非常不幸)。