开发者

JAVA中wait()和notify()如何使用详解

目录
  • 前言
  • 一、wait()方法
    • 1.wait()主要做的事
    • 2.wait()的结束条件
    • 3.有参数的wait()
  • 二、notify()
    • 1.notify()主要做的事
    • 2.notify() 会唤醒sleep()吗?
    • 3.notifyAll()
  • 三、调用 wait\notify\synchronized 使用的对象
    • 四、wait和sleep的比较/区别(面试题)
      • 总结

        前言

        大家应该都知道,线程之间是抢占式随机执行的,但是我们并不希望这样。因为这样非常混乱,并不好去预估程序的执行结果。我们甚至希望,线程之间能够配合执行,那么我们就可以使用wait()和notify()来做到。

        一、wait()方法

        wait有两个方法:

        wait():让当前线程进入等待(阻塞)状态。死等,没有唤醒就会一直阻塞

        wait(long timeout) :指定时间内,让线程进入等待(阻塞)状态。

        1.wait()主要做的事

        • 使当前执行代码的线程进行等待。(把线程放到等待队列中)
        • 释放当前的锁
        • 满足条件被notify()唤醒,重新尝试获取这个锁
            public static void main(String[] args) throws InterruptedException {
                Object object = new Object();
        
                System.out.println("wait 之前");
        
                /**
                 * Object中有wait方法,通过Object对象调用wait方法
                 * wait没有参数的版本是默认死等
                 * wait带参数的版本是指定超时时间,如果超时了还没有notify就继续执行
                 *
                 * 使用wait有三个操作:
                 * (1)释放锁
                 * (2)进入阻塞等待,准备接受通知
                 * (3)收到通知之后唤醒,并且重新获取锁
                 */
        
                /**
                 * 因为wait会解锁,所以wait必须在synchronized内部
                 * 而且synchronized括号内的Object对象,必须和调用wait的是同一对象
                 */
                synchronized(object) {
                    object.wait();
                }
                System.out.println("wait 之后");
        
            }

        JAVA中wait()和notify()如何使用详解

        2.wait()的结束条件

        其他线程调⽤该对象的 notify ⽅法.

        wait 等待时间超时 (wait ⽅法提供⼀个带有 timeout 参数的版本, 来指定等待时间).

        其他线程调⽤该等待线程的 interrupt ⽅法, 导致 wait 抛出 InterruptedException 异常,并清除中断标志位(给程序员自由发挥的空间),并重新尝试获取锁。
            public static void main(String[] args) throws InterruptedException {
                Object locker = new Object();
        
                Thread t1 = new Thread(() -> {
                    //获取当前线程
                    Thread current = Thread.currentThread();
                    int count = 0;
                    //标志位默认为false,为true就是被中断了
                    while (!current.isInterrupted()) {
                        //如果中断位被清空了,不会执行第二次
                        System.out.println("t1线程第一次执行" + count++);
                        synchronized (locker) {
                            //因为Thread并没有处理这个异常,所以必须在这里使用try-catch处理一下
                            try {
                                //开始等待
                                //如果被interrupt中断会抛出异常,并清除中断位
                                //并重新尝试获取锁
                                locker.wait();
                            } catch (InterruptedException e) {
                                System.err.println("被interrupt唤醒");
                            }
                        }
                    }
                });
        
                t1.start();
                while (true) {
                    Thread.sleep(1000);
                    t1.interrupt();
                }
            }

        3.有参数的wait()

            public static void main(String[] args) {
                Object locker = n编程客栈ew Object();
        
                Thread t1 = new Thread(() -> {
                    long start = System.currentTimeMillis();
                    System.out.println("wait之前");
                    synchronized (locker) {
                        try {
                            locker.wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    long end = System.currentTimeMillis();
                    System.out.println("wait之后 耗时:" + (end - start));
                });
        
                t1.start();
        
            }

        结果:

        JAVA中wait()和notify()如何使用详解

        二、notify()

        notify⽅法是唤醒等待的线程,主要配合wait():

        notify():唤醒同一对象调用正在wait()的线程,如果有多个线程正在wait()就会随机唤醒一个线程

        notifyAll():唤醒所有正在wait()的线程

        1.notify()主要做的事

        ⽅法notify()也要在同步⽅法或同步块中调⽤,该⽅法是⽤来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。

        如果有多个线程等待,则有线程调度器随机挑选出⼀个呈 wait 状态的线程。(并没有 "先来后到")

        在notify()⽅法后,当前线程不会⻢上释放该对象锁,要等到执⾏notify()⽅法的线程将程序执⾏完,也就是退出同步代码块之后才会释放对象锁。

            public static void main(String[] args) throws InterruptedException {
                Object locker = new Object();
        
        
                Thread t1 = new Thread(() -> {
                    synchronized (locker) {
        
                        System.out.println("wait之前~");
        
                        try {
                         android   locker.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
        
                        System.out.println("wait之后!");
                    }
                });
        
        
                Thread t2 = new Thread(() -> {
                    synchronized (locker) {
        
                        System.out.println("notify之前");
        
                        locker.notify();
        
                        //notify之后,并不会马上释放锁结束,至少会
                        //把synchronized中的语句执行完
                        System.out.println("未解锁之后的notify");
                    }
                    //看是否会执行到这一条语句
                    System.err.println("解锁之后的notify");
                });
        
                t1.start();
                //防止执行到notify了,t2还没阻塞(wait)
                Thread.sleep(500);
                t2.start();
        
            }

        结果分析:

        JAVA中wait()和notify()如何使用详解

        注意事项:

        JAVA中wait()和notify()如何使用详解

        2.notify() 会唤醒sleep()吗?

            public static void main(String[] args) throws InterruptedException {
                Object locker = new Object();
        
                Thread t1 = new Thread(() -> {
                    System.out.println("sleep睡眠");
                    try {
                        //sleep不需要在synchronized里
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("sleep睡醒了");
                });
        
                Thread t2 = new Thread(() -> {
                    synchronized (locker) {
                        System.out.println("notify之前");
                        locker.notify();
                        System.out.println("notify之后");
                    }
                });
        
                t1.start();
                Thread.sleep(1000);
                t2.sta编程rt();
            }

        结果:

        JAVA中wait()和notify()如何使用详解

        3.notifyAll()

         唤醒所有正在等待的线程

            public static void main(String[] args) throws InterruptedException {
                Object locker = new Object();
                Thread t1 = new Thread(() -> {
                    System.out.println("t1线程开始");
                    synchronized (locker) {
                        try {
                            locker.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.err.println("t1线程结束");
                });
                Thread t2 = new Thread(() -> {
                    System.out.println("t2线程开始");
                    synchronized (locker) {
                        try {
                            locker.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.err.println("t2线程结束");
                });
                Thread t3 = new Thread(() -> {
                    System.out.println("t3线程开始");
                    synchronized (locker) {
                        try {
                            locker.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.err.println("t3线程结束");
                });
                Thread t4 = new Thread(() -> {
                    synchronized (locker) {
                        System.out.println("notify之前");
                        locker.notifyAll();
                        System.out.println("notify之后");
                    }
                });
        
                t1.start();
                t2.start();
                t3.start();
                //保证上面的线程都已经执行了wait
                Thread.sleep(1000);
                t4.start();
            }

        JAVA中wait()和notify()如何使用详解

        三、调用 wait\notify\synchronized 使用的对象

         注意:wait和notify都必须放在synchronized中,不然会抛出异常:IllegalMonitorStateException

            public static void main(String[] args) {
                Object locker = new Object();
        
                Thread t1 = new Thread(() -> {
                    System.out.println("t1线程开始");
                    try {
                        locker.wait();
                   编程客栈 } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.err.println("t1线程结束");
           编程客栈     });
        
                Thread t4 = new Thread(() -> {
                    try {
                        Thread.sleep(1000);
                        locker.notify();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
        
                t1.start();
                t4.start();
            }

        JAVA中wait()和notify()如何使用详解

        并且synchronized括号内部,必须和调用的是同一对象,不然依然会抛异常:

            public static void main(String[] args) {
                Object locker1 = new Object();
                Object locker2 = new Object();
        
                Thread t1 = new Thread(() -> {
                    synchronized (locker1) {
                        System.out.println("t1线程开始");
                        try {
                            locker2.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.err.println("t1线程结束");
                    }
                });
        
                Thread t4 = new Thread(() -> {
                    synchronized (locker1) {
                        try {
                            Thread.sleep(1000);
                            locker2.notify();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
        
                });
        
                t1.start();
                t4.start();
            }

        JAVA中wait()和notify()如何使用详解

        四、wait和sleep的比较/区别(面试题)

        其实理论上 wait 和 sleep 完全是没有可⽐性的,因为⼀个是⽤于线程之间的通信的,⼀个是让线程阻塞⼀段时间。

        相同点:

        都会让线程放弃执行一段时间

        都可以被interrupt唤醒,并且都会抛出 InterruptedException 异常,并且清空标志位

        不同点:

        wait是Object的方法,sleep是Thread的静态方法

        wait必须在synchronized中,sleep不需要

        wait阻塞的时候释放锁,sleep并不会,sleep会抱着锁一起阻塞

        wait用于线程间通信(如生产者-消费者模型),sleep用于阻塞线程

        总结

        到此这篇关于Java中wait()和notify()如何使用的文章就介绍到这了,更多相关Java wait()和notify()内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新开发

        开发排行榜