notify() 如何按顺序或随机唤醒线程
how the notify() wake up thread in order or at random
我知道notify
会随机唤醒处于等待状态的线程。但是看下面的代码
public class ThreadTest {
public static void main(String[] args) {
Object co = new Object();
System.out.println(co);
for (int i = 0; i < 1000; i++) {
MyThread t = new MyThread("Thread" + i, co);
t.start();
}
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("-----Main Thread notify-----");
synchronized (co) {
co.notify();
}
TimeUnit.SECONDS.sleep(2);
System.out.println("Main Thread is end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class MyThread extends Thread {
private String name;
private Object co;
public MyThread(String name, Object o) {
this.name = name;
this.co = o;
}
@Override
public void run() {
try {
synchronized (co) {
System.out.println(name + " is waiting.");
co.wait();
System.out.println(name + " has been notified.");
co.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
但线程按调用顺序唤醒 wait()
。表示有人先呼叫wait()
,它先醒来;有人叫 wait()
第二,它弱了第二...
我认为是我的代码错误造成的,但我不知道问题出在哪里。
为了方便展示,我把迭代次数从20次改成1000次,结果是一样的
code.thread.ThreadTest
java.lang.Object@45ee12a7
Thread0 is waiting.
Thread1 is waiting.
Thread2 is waiting.
Thread3 is waiting.
Thread4 is waiting.
Thread5 is waiting.
Thread6 is waiting.
Thread7 is waiting.
Thread9 is waiting.
Thread10 is waiting.
Thread11 is waiting.
Thread8 is waiting.
Thread12 is waiting.
Thread13 is waiting.
Thread14 is waiting.
Thread15 is waiting.
Thread16 is waiting.
Thread17 is waiting.
Thread18 is waiting.
Thread19 is waiting.
-----Main Thread notify-----
Thread0 has been notified.
Thread1 has been notified.
Thread2 has been notified.
Thread3 has been notified.
Thread4 has been notified.
Thread5 has been notified.
Thread6 has been notified.
Thread7 has been notified.
Thread9 has been notified.
Thread10 has been notified.
Thread11 has been notified.
Thread8 has been notified.
Thread12 has been notified.
Thread13 has been notified.
Thread14 has been notified.
Thread15 has been notified.
Thread16 has been notified.
Thread17 has been notified.
Thread18 has been notified.
Thread19 has been notified.
Main Thread is end.
Process finished with exit code 0
The specification of notify()
说:
If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation.
这意味着您不能期望任何特定顺序,而实现可以使用它想要的任何特定顺序,包括线程调用的顺序 wait
。
一个实现没有理由应该实现改组。那只会浪费资源。给予实现自由的原因是允许他们在合适的时候使用更有效的算法,而不必维护顺序。
因此,由于不同的存储结构,不同的实现可能会以相反的顺序唤醒它们。如果实现切换到高于阈值的不同存储结构,它也可能随着一定数量的排队线程而改变。
除此之外,你的测试代码很特别。您的主线程在调用 notify()
之前安静等待了很长时间,因此很可能所有线程都已经进入等待状态并存储在 JVM 使用的任何数据结构中。然后,您一次只有一个待处理的 notify()
,因为您让已被唤醒的线程执行下一个 notify()
。如果允许操作重叠,情况可能会发生巨大变化。
那么,可能会发现底层数据结构并不是一个纯粹的FIFO。此外,实现允许调用 wait()
的线程在有未决的 notify()
时立即继续执行是很常见的,而不考虑等待队列,绕过所有已经排队的线程,因为这样效率更高。
还有一点就是,如果有多个pendingnotify()
,被唤醒的线程就必须竞争重新获取object monitor。这取决于操作系统的调度程序和实际的系统负载,哪个线程会在这里成功,所以即使线程按照它们入队的顺序被唤醒,由于 JVM 无法控制的细节,线程也可能在这个地方超车。
此外,不要忘记规范允许 虚假唤醒。因此,无法单独唤醒单个线程的 JVM 可以在不违反规范的情况下由于单个 notify()
调用而唤醒多个线程。
我知道notify
会随机唤醒处于等待状态的线程。但是看下面的代码
public class ThreadTest {
public static void main(String[] args) {
Object co = new Object();
System.out.println(co);
for (int i = 0; i < 1000; i++) {
MyThread t = new MyThread("Thread" + i, co);
t.start();
}
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("-----Main Thread notify-----");
synchronized (co) {
co.notify();
}
TimeUnit.SECONDS.sleep(2);
System.out.println("Main Thread is end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class MyThread extends Thread {
private String name;
private Object co;
public MyThread(String name, Object o) {
this.name = name;
this.co = o;
}
@Override
public void run() {
try {
synchronized (co) {
System.out.println(name + " is waiting.");
co.wait();
System.out.println(name + " has been notified.");
co.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
但线程按调用顺序唤醒 wait()
。表示有人先呼叫wait()
,它先醒来;有人叫 wait()
第二,它弱了第二...
我认为是我的代码错误造成的,但我不知道问题出在哪里。
为了方便展示,我把迭代次数从20次改成1000次,结果是一样的
code.thread.ThreadTest
java.lang.Object@45ee12a7
Thread0 is waiting.
Thread1 is waiting.
Thread2 is waiting.
Thread3 is waiting.
Thread4 is waiting.
Thread5 is waiting.
Thread6 is waiting.
Thread7 is waiting.
Thread9 is waiting.
Thread10 is waiting.
Thread11 is waiting.
Thread8 is waiting.
Thread12 is waiting.
Thread13 is waiting.
Thread14 is waiting.
Thread15 is waiting.
Thread16 is waiting.
Thread17 is waiting.
Thread18 is waiting.
Thread19 is waiting.
-----Main Thread notify-----
Thread0 has been notified.
Thread1 has been notified.
Thread2 has been notified.
Thread3 has been notified.
Thread4 has been notified.
Thread5 has been notified.
Thread6 has been notified.
Thread7 has been notified.
Thread9 has been notified.
Thread10 has been notified.
Thread11 has been notified.
Thread8 has been notified.
Thread12 has been notified.
Thread13 has been notified.
Thread14 has been notified.
Thread15 has been notified.
Thread16 has been notified.
Thread17 has been notified.
Thread18 has been notified.
Thread19 has been notified.
Main Thread is end.
Process finished with exit code 0
The specification of notify()
说:
If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation.
这意味着您不能期望任何特定顺序,而实现可以使用它想要的任何特定顺序,包括线程调用的顺序 wait
。
一个实现没有理由应该实现改组。那只会浪费资源。给予实现自由的原因是允许他们在合适的时候使用更有效的算法,而不必维护顺序。
因此,由于不同的存储结构,不同的实现可能会以相反的顺序唤醒它们。如果实现切换到高于阈值的不同存储结构,它也可能随着一定数量的排队线程而改变。
除此之外,你的测试代码很特别。您的主线程在调用 notify()
之前安静等待了很长时间,因此很可能所有线程都已经进入等待状态并存储在 JVM 使用的任何数据结构中。然后,您一次只有一个待处理的 notify()
,因为您让已被唤醒的线程执行下一个 notify()
。如果允许操作重叠,情况可能会发生巨大变化。
那么,可能会发现底层数据结构并不是一个纯粹的FIFO。此外,实现允许调用 wait()
的线程在有未决的 notify()
时立即继续执行是很常见的,而不考虑等待队列,绕过所有已经排队的线程,因为这样效率更高。
还有一点就是,如果有多个pendingnotify()
,被唤醒的线程就必须竞争重新获取object monitor。这取决于操作系统的调度程序和实际的系统负载,哪个线程会在这里成功,所以即使线程按照它们入队的顺序被唤醒,由于 JVM 无法控制的细节,线程也可能在这个地方超车。
此外,不要忘记规范允许 虚假唤醒。因此,无法单独唤醒单个线程的 JVM 可以在不违反规范的情况下由于单个 notify()
调用而唤醒多个线程。