【问题标题】:Calling Java notify() within an if condition在 if 条件内调用 Java notify()
【发布时间】:2016-09-10 06:01:51
【问题描述】:

我刚刚写了一个简单的java示例来熟悉等待和通知方法的概念。

想法是当调用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。

非常感谢任何帮助。

【问题讨论】:

  • notify 不释放锁。
  • 您能否进一步说明这一点。
  • 线程没有离开同步块。因此,尽管等待可能返回,但 main 将无法继续运行。
  • 谢谢大家。明白了。好吧,在这种情况下,我可以使用 join() 代替。

标签: java multithreading wait notify


【解决方案1】:

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

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

  1. 主线程获取锁。
  2. 主线程启动第二个线程。
  3. 第二个线程命中同步块,但由于主线程正在持有锁,因此无法进入同步块。
  4. 主线程调用wait()。此调用释放锁并将主线程置于WAITING 状态。
  5. 第二个线程现在可以获取锁并进入同步块。
  6. 第二个线程计数为 5 并调用 notify()。此调用不会释放锁,它只是通知主线程它可以在重新获得锁后立即进行。
  7. 主线程已唤醒但无法取得进展,因为它无法重新获得锁(它仍被第二个线程持有)。请记住,在同一个锁保护的同步块中,没有两个线程可以同时处于活动状态,现在,第二个线程仍然处于活动状态,因此主线程必须继续等待。
  8. 第二个线程继续计数,将 times 设置为 10,最终离开同步块,释放锁。
  9. 主线程重新获得锁,现在可以进入println。但是此时,times 已经是 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 volatile,否则JVM 不能保证你会在你的主线程中看到它的实际值。

您还应该了解,这种方法并不能保证您的主线程打印 5。它可能会在到达 println 调用时,第二个线程进行一两次甚至更多次迭代,而您' 会看到大于 5 的值(尽管由于每次迭代都会调用 sleep() ,这是非常不幸的)。

【讨论】:

  • 它就像一个魅力。谢谢安德鲁。非常感谢
猜你喜欢
  • 2012-08-18
  • 2012-05-10
  • 2020-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-16
  • 2017-08-30
  • 1970-01-01
相关资源
最近更新 更多