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

  • 相关阅读:
    4.28综合练习
    团队项目第一阶段冲刺第六天
    4.27防盗链和代理
    梦断代码阅读笔记3
    团队项目第一阶段冲刺第五天
    4.26抓取猪⼋戒数据
    团队项目第一阶段冲刺第四天
    4.25xpath解析
    4.24aiohttp模块学习
    如何将类数组转化为数组?
  • 原文地址:https://www.cnblogs.com/dalianpai/p/14206812.html
Copyright © 2011-2022 走看看