在 if 条件下调用 Java notify()
Calling Java notify() within an if condition
我刚刚编写了一个简单的 java 示例来熟悉等待和通知方法的概念。
思路是在调用notify()
时,主线程会打印出总和。
我的线程class
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();
}
}
}
}
主要Class
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 循环,将 times 的值更新为 10。
非常感谢任何帮助。
同步块意味着互斥。在任何给定时刻,只允许一个线程持有锁并执行同步块中的代码。此规则遍及由同一把锁保护的所有块。
在你的例子中,有两个这样的块使用相同的锁,所以它要么是主线程,要么是 MyThread
被允许在这些块中的任何一个中执行代码,另一个线程必须等待。所以,你在这里有以下场景:
- 主线程获取锁
- 主线程启动第二线程
- 第二个线程命中同步块但无法进入同步块,因为主线程正在持有锁。
- 主线程调用
wait()
。此调用释放锁并将主线程置于 WAITING
状态。
- 第二个线程现在可以获取锁并进入同步块。
- 第二个线程数到五并调用
notify()
。这个调用并没有释放锁,它只是通知主线程一旦它可以重新获取锁就可以继续进行了。
- 主线程被唤醒,但无法继续,因为它无法重新获取锁(它仍然被第二个线程持有)。请记住,在由同一个锁保护的同步块中,没有两个线程可以同时处于活动状态,现在,第二个线程仍然处于活动状态,因此主线程必须继续等待。
- 第二个线程继续计数,将
times
设置为10并最终离开同步块,释放锁。
- 主线程重新获取锁,现在可以进入
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
可变,否则 JVM 无法保证您会在主线程中看到它的实际值。
而且您还应该明白,这种方法并不能保证您的主线程打印 5。可能会发生这样的情况,即在到达 println
调用时,第二个线程会打印一个或两个甚至更多的迭代,你会看到大于 5 的东西(尽管由于每次迭代都会调用 sleep()
而非常不幸)。
我刚刚编写了一个简单的 java 示例来熟悉等待和通知方法的概念。
思路是在调用notify()
时,主线程会打印出总和。
我的线程class
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();
}
}
}
}
主要Class
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 循环,将 times 的值更新为 10。
非常感谢任何帮助。
同步块意味着互斥。在任何给定时刻,只允许一个线程持有锁并执行同步块中的代码。此规则遍及由同一把锁保护的所有块。
在你的例子中,有两个这样的块使用相同的锁,所以它要么是主线程,要么是 MyThread
被允许在这些块中的任何一个中执行代码,另一个线程必须等待。所以,你在这里有以下场景:
- 主线程获取锁
- 主线程启动第二线程
- 第二个线程命中同步块但无法进入同步块,因为主线程正在持有锁。
- 主线程调用
wait()
。此调用释放锁并将主线程置于WAITING
状态。 - 第二个线程现在可以获取锁并进入同步块。
- 第二个线程数到五并调用
notify()
。这个调用并没有释放锁,它只是通知主线程一旦它可以重新获取锁就可以继续进行了。 - 主线程被唤醒,但无法继续,因为它无法重新获取锁(它仍然被第二个线程持有)。请记住,在由同一个锁保护的同步块中,没有两个线程可以同时处于活动状态,现在,第二个线程仍然处于活动状态,因此主线程必须继续等待。
- 第二个线程继续计数,将
times
设置为10并最终离开同步块,释放锁。 - 主线程重新获取锁,现在可以进入
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
可变,否则 JVM 无法保证您会在主线程中看到它的实际值。
而且您还应该明白,这种方法并不能保证您的主线程打印 5。可能会发生这样的情况,即在到达 println
调用时,第二个线程会打印一个或两个甚至更多的迭代,你会看到大于 5 的东西(尽管由于每次迭代都会调用 sleep()
而非常不幸)。