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次,但结果相同。


DIEA
浏览 278回答 1
1回答

慕的地8271018

规格notify()说:如果有任何线程在此对象上等待,则选择其中一个唤醒。该选择是任意的,并且可以根据实现情况进行选择。这意味着您不能期望任何特定的顺序,而实现可能会使用它想要的任何特定的顺序,包括调用的顺序wait。没有理由为什么实现应该实现改组。那只会浪费资源。使实现具有自由的原因是允许他们在适合时使用更高效的算法,而不必维护命令。因此,由于存储结构不同,不同的实现可能会以相反的顺序唤醒它们。如果实现切换到阈值以上的其他存储结构,则它也可能随着一定数量的排队线程而改变。除此之外,您的测试代码非常特别。您的主线程在调用之前会等待很长时间notify(),因此所有线程可能已经进入等待状态,并存储在JVM使用的任何数据结构中。然后,一次只有一个挂起notify(),因为让唤醒的线程进行下一个挂起notify()。如果您允许操作重叠,则图片可能会发生巨大变化。然后,可能会发现底层数据结构不是纯FIFO。同样,对于实现来说,wait()如果有待处理的线程允许线程调用立即继续进行notify(),而不考虑等待队列,而绕过所有已排队的线程,这是一种安静的做法,因为这样做效率更高。另一点是,如果有多个挂起notify(),则唤醒的线程必须竞争重新获取对象监视器。这取决于操作系统的调度程序和实际的系统负载,哪个线程将在此处成功执行,因此,即使按入队的顺序唤醒了这些线程,由于JVM无法控制的详细信息,线程仍可能在此位置被超越。另外,请不要忘记规范允许虚假唤醒。因此,一个不能够单独唤醒单个线程的JVM可以在notify()不违反规范的情况下,由于一次调用而唤醒多个线程。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java