美文网首页
Wait和Notify

Wait和Notify

作者: 程序设计法师 | 来源:发表于2018-12-25 09:46 被阅读0次
    Wait和Notify并不是Thread特有的方法,而是Object中的方法,也就是说在JDK中每一个类都拥有这两个方法,那么这两个方法到底有什么神奇之处可以使线程阻塞又可以唤醒线程呢?
    • wait(long timeout)方法会导致当前线程进入阻塞,直到有其他线程调用了Object的notify或者notifyAll方法才能将其唤醒,或者阻塞时间达到了timeout而自动唤醒。
    • wait方法必须拥有该对象的monitor,也就是说wait方法必须在同步方法中使用
    • 一旦线程执行了某个Object的wait方法之后他就会释放对该对象的monitor的所有权,其他线程也会有机会继续争抢该monitor的所有权
    • 同步代码的monitor必须与执行wait notify方法的对象一致,简单的说就是用哪个对象中的monitor进行同步,就只能用哪个对象进行wait和notify操作,运行如下代码将会抛出IllegalMonitorStateException异常信息,因为monitor引用的是this,而wait使用的却是MUTEX的方法。
    private final Object MUTEX=new Object();
    private synchronized void testWait(){
        try{
                MUTEX.wait();
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
    }
    
    • wait方法是可中断方法,当前线程一旦调用了wait方法进入阻塞状态,其他线程是可以使用interrupt方法将其打断的,可中断方法被打断之后会收到中断异常,InterruptedException,同时interrupt标识也会被擦除。
    public class EventQueue {
        private  int max=0;
        private static final int DEFAULT_MAX_EVENT=10;
        static class Event{
    
        }
        private final LinkedList<Event> eventQueue=new LinkedList<>();
    
        public EventQueue(){
            this(DEFAULT_MAX_EVENT);
        }
        public EventQueue(int max){
            this.max=max;
        }
        //进入队列
        public void offer(Event event){
            synchronized (eventQueue){
                if(eventQueue.size()>=max){
                    try {
                        System.out.println("队列已满");
                        eventQueue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("the new event is submmited");
                eventQueue.addLast(event);
                eventQueue.notify();
            }
        }
        //从队列中取出
        public Event take(){
            synchronized (eventQueue){
                if(eventQueue.isEmpty()){
                    try {
                        System.out.println("队列是空的");
                        eventQueue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                Event event=eventQueue.removeFirst();
                this.eventQueue.notify();
                System.out.println("the event"+event+"isHandled");
                return event;
            }
        }
    
    }
    
    public class Test {
        public static void main(String [] args){
            final EventQueue eventQueue=new EventQueue();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (;;){
                        eventQueue.offer(new EventQueue.Event());
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (;;){
                        eventQueue.take();
                        try {
                            TimeUnit.SECONDS.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }
    
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    队列已满
    the eventwaitAndnotify.EventQueue$Event@20ff4faaisHandled
    the new event is submmited
    队列已满
    the eventwaitAndnotify.EventQueue$Event@2e38b5c5isHandled
    the new event is submmited
    队列已满
    the eventwaitAndnotify.EventQueue$Event@2a0724daisHandled
    the new event is submmited
    队列已满
    the eventwaitAndnotify.EventQueue$Event@6f05bffeisHandled
    the new event is submmited
    队列已满
    

    以上是单线程下的wait和notify的测试,但是如果是多线程,多条Thread同时offer,多条Thread同时take,则会出现读取混乱的现象

    public class Test {
        public static void main(String [] args){
            final EventQueue eventQueue=new EventQueue();
            for (int i = 0; i <3 ; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (;;){
                            eventQueue.offer(new EventQueue.Event());
                        }
                    }
                }).start();
            }
            for (int i = 0; i < 3; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (;;){
                            eventQueue.take();
                            try {
                                TimeUnit.SECONDS.sleep(10);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
        }
    }
    

    我们同时各开了三条线程去读取,可以看下打印日志

    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    队列已满
    队列已满
    the eventwaitAndnotify.EventQueue$Event@2f4c2608isHandled
    the new event is submmited
    队列已满
    the new event is submmited
    队列已满
    the new event is submmited
    队列已满
    the new event is submmited
    队列已满
    the new event is submmited
    队列已满
    the new event is submmited
    队列已满
    the new event is submmited
    队列已满
    the new event is submmited
    队列已满
    the new event is submmited
    队列已满
    

    如上我们可以看到队列满了之后依然会往里offer event,显示是不合理的。为什么会出现这个问题?
    假设某个时刻,EventQueue中存在10个Event数据,其中两个线程在执行offer方法的时候分别因为调用了wait方法而进入阻塞,另外一个线程执行take方法消费了event元素并且唤醒了一个offer线程,而该offer线程执行了addLast之后,queue中的元素为10,并且再次执行唤醒方法,恰巧另外一个offer线程也被唤醒,因此可以绕开阈值检查eventQueue.size()>=max,致使EventQueue中的元素超过10个
    这里我们只需要将if改成while,notify改成notifyAll即可

    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    the new event is submmited
    队列已满
    队列已满
    the eventwaitAndnotify.EventQueue$Event@663a8ec8isHandled
    the eventwaitAndnotify.EventQueue$Event@6a3899f6isHandled
    the eventwaitAndnotify.EventQueue$Event@50230424isHandled
    the new event is submmited
    the new event is submmited
    the new event is submmited
    队列已满
    队列已满
    队列已满
    the eventwaitAndnotify.EventQueue$Event@64ff7ac1isHandled
    the eventwaitAndnotify.EventQueue$Event@5ee4e050isHandled
    the eventwaitAndnotify.EventQueue$Event@68fd6c7fisHandled
    the new event is submmited
    the new event is submmited
    the new event is submmited
    队列已满
    队列已满
    队列已满
    the eventwaitAndnotify.EventQueue$Event@60c0f286isHandled
    the eventwaitAndnotify.EventQueue$Event@6cc2e5b8isHandled
    the eventwaitAndnotify.EventQueue$Event@5adb6f54isHandled
    the new event is submmited
    the new event is submmited
    the new event is submmited
    队列已满
    队列已满
    队列已满
    

    为什么使用while而不使用if呢?是因为当线程从wait中唤醒时,那么将继续处理他的业务逻辑(当我们的if或者while条件不满足时),如果使用if(只执行一次)走过了判断条件,将要走业务逻辑的时候,他是不可能回去再执行判断条件的,但是如果是while的话,如果当前线程从wait中唤醒时,他将继续执行判断条件然后再去执行业务逻辑
    https://blog.csdn.net/qq_36974281/article/details/81951006

    synchronized (monitor) {
        //判断条件是否得到满足
        if(eventQueue.size()>=max) {
            //等待唤醒
            monitor.wait();
        }
        //业务逻辑
    }
    

    相关文章

      网友评论

          本文标题:Wait和Notify

          本文链接:https://www.haomeiwen.com/subject/leexlqtx.html