zoukankan      html  css  js  c++  java
  • wait和notify

    wait notify 原理

    image-20201229144719694

    • Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态
    • BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片
    • BLOCKED 线程会在 Owner 线程释放锁时唤醒
    • WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList 重新竞争

    API:

    • obj.wait() 让进入 object 监视器的线程到 waitSet 等待
    • obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
    • obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒

    sleep(long n) 和 wait(long n) 的区别:

    1. sleep 是 Thread 方法,而 wait 是 Object 的方法

    2. sleep 不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用

    3. sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁

    4. 它们状态 TIMED_WAITING

    演示:

    1.示例代码

    import lombok.extern.slf4j.Slf4j;
    import static com.dalianpai.Sleeper.sleep;
    /**
     * @author WGR
     * @create 2020/12/29 -- 14:57
     */
    @Slf4j(topic = "c.TestCorrectPosture")
    public class TestCorrectPostureStep1 {
        static final Object room = new Object();
        static boolean hasCigarette = false; // 有没有烟
        static boolean hasTakeout = false;
    
        public static void main(String[] args) {
            new Thread(() -> {
                synchronized (room) {
                    log.debug("有烟没?[{}]", hasCigarette);
                    if (!hasCigarette) {
                        log.debug("没烟,先歇会!");
                        sleep(2);
                    }
                    log.debug("有烟没?[{}]", hasCigarette);
                    if (hasCigarette) {
                        log.debug("可以开始干活了");
                    }
                }
            }, "小南").start();
    
    
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    synchronized (room) {
                        log.debug("可以开始干活了");
                    }
                }, "其它人").start();
            }
    
            sleep(1);
            new Thread(() -> {
                synchronized (room) {
                    hasCigarette = true;
                    log.debug("烟到了噢!");
                }
            }, "送烟的").start();
        }
    
    }
    

    image-20201229150532024

    其它干活的线程,都要一直阻塞,效率太低
    小南线程必须睡足 2s 后才能醒来,就算烟提前送到,也无法立刻醒来
    加了 synchronized (room) 后,就好比小南在里面反锁了门睡觉,烟根本没法送进门,main 没加synchronized 就好像 main 线程是翻窗户进来的
    解决方法,使用 wait - notify 机制

    2.示例代码

    import lombok.extern.slf4j.Slf4j;
    import static com.dalianpai.Sleeper.sleep;
    /**
     * @author WGR
     * @create 2020/12/29 -- 15:13
     */
    @Slf4j(topic = "c.TestCorrectPosture")
    public class TestCorrectPostureStep2 {
        static final Object room = new Object();
        static boolean hasCigarette = false;
        static boolean hasTakeout = false;
    
        public static void main(String[] args) {
            new Thread(() -> {
                synchronized (room) {
                    log.debug("有烟没?[{}]", hasCigarette);
                    if (!hasCigarette) {
                        log.debug("没烟,先歇会!");
                        try {
                            room.wait(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    log.debug("有烟没?[{}]", hasCigarette);
                    if (hasCigarette) {
                        log.debug("可以开始干活了");
                    }
                }
            }, "小南").start();
    
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    synchronized (room) {
                        log.debug("可以开始干活了");
                    }
                }, "其它人").start();
            }
    
            sleep(1);
            new Thread(() -> {
                synchronized (room) {
                    hasCigarette = true;
                    log.debug("烟到了噢!");
                    room.notify();
                }
            }, "送烟的").start();
        }
    
    }
    
    

    image-20201229151542453

    解决了其它干活的线程阻塞的问题,但如果有其它线程也在等待条件呢?

    3.示例代码

    import lombok.extern.slf4j.Slf4j;
    import static com.dalianpai.Sleeper.sleep;
    /**
     * @author WGR
     * @create 2020/12/29 -- 15:17
     */
    @Slf4j(topic = "c.TestCorrectPosture")
    public class TestCorrectPostureStep3 {
        static final Object room = new Object();
        static boolean hasCigarette = false;
        static boolean hasTakeout = false;
    
        // 虚假唤醒
        public static void main(String[] args) {
            new Thread(() -> {
                synchronized (room) {
                    log.debug("有烟没?[{}]", hasCigarette);
                    if (!hasCigarette) {
                        log.debug("没烟,先歇会!");
                        try {
                            room.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    log.debug("有烟没?[{}]", hasCigarette);
                    if (hasCigarette) {
                        log.debug("可以开始干活了");
                    } else {
                        log.debug("没干成活...");
                    }
                }
            }, "小南").start();
    
            new Thread(() -> {
                synchronized (room) {
                    Thread thread = Thread.currentThread();
                    log.debug("外卖送到没?[{}]", hasTakeout);
                    if (!hasTakeout) {
                        log.debug("没外卖,先歇会!");
                        try {
                            room.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    log.debug("外卖送到没?[{}]", hasTakeout);
                    if (hasTakeout) {
                        log.debug("可以开始干活了");
                    } else {
                        log.debug("没干成活...");
                    }
                }
            }, "小北").start();
    
            sleep(1);
            new Thread(() -> {
                synchronized (room) {
                    hasTakeout = true;
                    log.debug("外卖到了噢!");
                    room.notifyAll();
                }
            }, "送外卖的").start();
    
    
        }
    
    }
    
    

    用 notifyAll 仅解决某个线程的唤醒问题,但使用 if + wait 判断仅有一次机会,一旦条件不成立,就没有重新判断的机会了
    解决方法,用 while + wait,当条件不成立,再次 wait

    4.示例代码

    import lombok.extern.slf4j.Slf4j;
    import static com.dalianpai.Sleeper.sleep;
    /**
     * @author WGR
     * @create 2020/12/29 -- 15:19
     */
    @Slf4j(topic = "c.TestCorrectPosture")
    public class TestCorrectPostureStep5 {
        static final Object room = new Object();
        static boolean hasCigarette = false;
        static boolean hasTakeout = false;
    
        public static void main(String[] args) {
    
    
            new Thread(() -> {
                synchronized (room) {
                    log.debug("有烟没?[{}]", hasCigarette);
                    while (!hasCigarette) {
                        log.debug("没烟,先歇会!");
                        try {
                            room.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    log.debug("有烟没?[{}]", hasCigarette);
                    if (hasCigarette) {
                        log.debug("可以开始干活了");
                    } else {
                        log.debug("没干成活...");
                    }
                }
            }, "小南").start();
    
            new Thread(() -> {
                synchronized (room) {
                    Thread thread = Thread.currentThread();
                    log.debug("外卖送到没?[{}]", hasTakeout);
                    while (!hasTakeout) {
                        log.debug("没外卖,先歇会!");
                        try {
                            room.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    log.debug("外卖送到没?[{}]", hasTakeout);
                    if (hasTakeout) {
                        log.debug("可以开始干活了");
                    } else {
                        log.debug("没干成活...");
                    }
                }
            }, "小女").start();
    
            sleep(1);
            new Thread(() -> {
                synchronized (room) {
                    hasTakeout = true;
                    log.debug("外卖到了噢!");
                    room.notifyAll();
                }
            }, "送外卖的").start();
    
    
        }
    
    }
    
    

    image-20201229152030629

  • 相关阅读:
    【转】【SEE】基于SSE指令集的程序设计简介
    【转】【Asp.Net】asp.net服务器控件创建
    ControlTemplate in WPF ——ScrollBar
    ControlTemplate in WPF —— Menu
    ControlTemplate in WPF —— Expander
    ControlTemplate in WPF —— TreeView
    ControlTemplate in WPF —— ListBox
    ControlTemplate in WPF —— ComboBox
    ControlTemplate in WPF —— TextBox
    ControlTemplate in WPF —— RadioButton
  • 原文地址:https://www.cnblogs.com/dalianpai/p/14206812.html
Copyright © 2011-2022 走看看