在if条件中调用Java notify()

Maz*_*zen 4 java multithreading notify wait

我刚刚编写了一个简单的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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

主类

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

预期成绩

5但我得了10.

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

任何帮助都非常感谢.

And*_*gin 5

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

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

  1. 主线程获取锁定.
  2. 主线程启动第二个线程.
  3. 第二个线程命中同步块,但由于主线程正在保持锁定,因此无法输入它.
  4. 主线程调用wait().此调用释放锁并将主线程置于WAITING状态.
  5. 第二个线程现在可以获取锁并进入同步块.
  6. 第二个线程计为五个并调用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

不要忘记使用timesvolatile,否则JVM不能保证你会在主线程中看到它的实际值.

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