为了账号安全,请及时绑定邮箱和手机立即绑定

notify()如何按顺序或随机唤醒线程

notify()如何按顺序或随机唤醒线程

DIEA 2021-05-09 16:29:18
我知道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次,但结果相同。
查看完整描述

1 回答

?
慕的地8271018

TA贡献1796条经验 获得超4个赞

规格notify()说:

如果有任何线程在此对象上等待,则选择其中一个唤醒。该选择是任意的,并且可以根据实现情况进行选择。

这意味着您不能期望任何特定的顺序,而实现可能会使用它想要的任何特定的顺序,包括调用的顺序wait

没有理由为什么实现应该实现改组。那只会浪费资源。使实现具有自由的原因是允许他们在适合时使用更高效的算法,而不必维护命令。

因此,由于存储结构不同,不同的实现可能会以相反的顺序唤醒它们。如果实现切换到阈值以上的其他存储结构,则它也可能随着一定数量的排队线程而改变。

除此之外,您的测试代码非常特别。您的主线程在调用之前会等待很长时间notify(),因此所有线程可能已经进入等待状态,并存储在JVM使用的任何数据结构中。然后,一次只有一个挂起notify(),因为让唤醒的线程进行下一个挂起notify()。如果您允许操作重叠,则图片可能会发生巨大变化。

然后,可能会发现底层数据结构不是纯FIFO。同样,对于实现来说,wait()如果有待处理的线程允许线程调用立即继续进行notify(),而不考虑等待队列,而绕过所有已排队的线程,这是一种安静的做法,因为这样做效率更高。

另一点是,如果有多个挂起notify(),则唤醒的线程必须竞争重新获取对象监视器。这取决于操作系统的调度程序和实际的系统负载,哪个线程将在此处成功执行,因此,即使按入队的顺序唤醒了这些线程,由于JVM无法控制的详细信息,线程仍可能在此位置被超越。

另外,请不要忘记规范允许虚假唤醒。因此,一个不能够单独唤醒单个线程的JVM可以在notify()不违反规范的情况下,由于一次调用而唤醒多个线程。


查看完整回答
反对 回复 2021-05-19
  • 1 回答
  • 0 关注
  • 277 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信