首页 > 其他分享 >wait和notify方法的使用

wait和notify方法的使用

时间:2022-11-05 18:11:20浏览次数:35  
标签:Thread lock t1 线程 notify new 方法 public wait

wait和notify方法用来实现一个线程需要等待另一个线程的执行结果的场景。

wait: 让当前线程在Monitor对象上等待,变成Waiting状态

notify:唤醒Monitor对象上等待的一个线程

notifyAll:唤醒Monitor对象上等待的全部线程

一、wait和notify方法的简单使用

假设有两个线程t1,t2,t1需要有烟才能干活,t2是来送烟的。

public class Test5 {
    public static void main(String[] args) throws InterruptedException {
        //是否有烟的标记,默认false
        final boolean[] isHaveSmoke = {false};
        Object lock = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    if (!isHaveSmoke[0]){
                        //没有烟时就等待
                        try {
                            //注意要在哪个锁对象上等待就要调用哪个对象的方法
                            //这里是lock上等待
                            //wait方法会释放锁
                            System.out.println("没烟开始等待");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //有烟后继续干活
                    System.out.println("继续干活,烟的状态:"+ isHaveSmoke[0]);
                }

            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                //线程t2来送烟,3s后把烟送到
                try {
                    //注意sleep不会释放锁,所以不放在synchronized中,让其他线程有机会执行
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (lock){
                    System.out.println("烟送到了:");
                    isHaveSmoke[0] =true;
                    lock.notify();
                }
            }
        },"t2");

        
        t1.start();
        t2.start();
        
    }
}

上边这段代码演示了t1等待t2的结果后才能执行。

二、虚假唤醒问题

上边这段代码,如果还有一个线程t3在烟还没送到前就执行了notifyAll方法,t1就会被提前唤醒然后执行后边的逻辑

public class Test5 {
    public static void main(String[] args) throws InterruptedException {
        //是否有烟的标记,默认false
        final boolean[] isHaveSmoke = {false};
        Object lock = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    if (!isHaveSmoke[0]){
                        //没有烟时就等待
                        try {
                            //注意要在哪个锁对象上等待就要调用哪个对象的方法
                            //这里是lock上等待
                            //wait方法会释放锁
                            System.out.println("没烟开始等待");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //有烟后继续干活
                    System.out.println("继续干活,烟的状态:"+ isHaveSmoke[0]);
                }

            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                //线程t2来送烟,3s后把烟送到
                try {
                    //注意sleep不会释放锁,所以不放在synchronized中,让其他线程有机会执行
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (lock){
                    System.out.println("烟送到了:");
                    isHaveSmoke[0] =true;
                    lock.notify();
                }
            }
        },"t2");

        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //睡1s是为了保证t1已经开始等待了
                    Thread.sleep(1000);
                    synchronized (lock){
                        System.out.println("t3唤醒全部线程");
                        lock.notifyAll();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"t3");
        t1.start();
        t2.start();
        t3.start();
    }
}

为了展示烟的状态,我在t1干活时打印了烟的状态,可以看到t1确实是被虚假唤醒的。针对这种问题,因为if判断只有一次机会,所以t1被虚假唤醒后就不能再次判断烟的状态了,所以可以把if判断改成while,这样当t1被虚假唤醒后会再次循环检测烟的状态,如果烟还没到,t1就会继续等待。

Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    while (!isHaveSmoke[0]){
                        //没有烟时就等待
                        try {
                            //注意要在哪个锁对象上等待就要调用哪个对象的方法
                            //这里是lock上等待
                            //wait方法会释放锁
                            System.out.println("没烟开始等待");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //有烟后继续干活
                    System.out.println("继续干活,烟的状态:"+ isHaveSmoke[0]);
                }
            }
        },"t1");

三、wait notify的原理

wait方法使用到了Monitor对象里的一个waitset属性,当一个线程调用了wait方法时就会进入Monitor的waitset进行等待,状态变成waiting并释放锁,当其他线程调用了唤醒方法后才会恢复执行。

四、保护性暂停

这是一种设计模式,用来让一个线程等待另一个线程的结果

public class Test6 {
    public static void main(String[] args) {
        GuardedObject guardedObject = new GuardedObject();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("等待结果:");
                Object response = guardedObject.getResponse(2000);
                System.out.println("等到的结果:"+response);
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("等待后设置结果");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                guardedObject.setResponse("hello");
            }
        });
        t1.start();
        t2.start();
    }
}
//这个对象用来在两个线程之间传递结果
class GuardedObject{

    private Object response;

    public synchronized void setResponse(Object response){
        this.response =response;
    }

    //超过一定时间后就不再等待
    public synchronized Object getResponse(long timeout){
        //记录开始等待的时间
        long start = System.currentTimeMillis();
        long pastTime=0;//已经等待的时间
        while (response==null){
            if(pastTime- timeout>=0){
                break;
            }
            try {
                //只等待还剩下的还没等够的时间
                wait(timeout-pastTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //等待结束时计算pastTime
            pastTime=System.currentTimeMillis()-start;
        }
        return response;
    }
}

注意 join方法的底层用的就是wait方法,就用了类似这样的一种等待方式

标签:Thread,lock,t1,线程,notify,new,方法,public,wait
From: https://www.cnblogs.com/chengxuxiaoyuan/p/16860761.html

相关文章

  • Collection 常用方法
          ......
  • 解决小程序中textarea ios端样式不兼容的两种方法
    今天遇到在小程序里面textarea会存在一定的兼容性问题,textarea有默认的内边距,在安卓手机和ios手机显示的时候,ios手机的内边距会比安卓手机的内边距大很多,所以会造成样式不兼......
  • Zabbix 5.0 监控 SQLSERVER的配置方法
    0x01环境说明zabbix server5.0  zabbixagent25.0.28windowsserver2019  防火墙已关闭,也可开启放行相关端口SQLSERVER2016 0x02配置准备freetds......
  • MacOS13系统升级动态壁纸无法安装解决方法
    MacOS13系统升级,导致网站上的壁纸无法双击安装,怎么办?这里小编为大家带来了解决方法,一起来看看!网站上下载的壁纸安装包打开,双击安装没有反应键盘上按住shiftcommand.三个键,找......
  • MyBatis--判断boolean类型实现动态sql--方法/实例
    简介        本文介绍MyBatis如何判断boolean类型实现动态sql。        使用MyBatis时,有时需要使用if标签判断boolean类型,从而决定是否拼接sql(动态查询)。代......
  • Spring Data MongoDB--MongoTemplate查询数据--方法/实例
    简介说明        本文用示例介绍SpringDataMongoDB(MongoTemplate)查询数据的方法。查询方法分类mongoTemplate.find //返回listmongoTemplate.findOne //返回1......
  • Spring Cloud LoadBalancer--自定义负载均衡策略--方法/实例
    简介说明    本文用示例介绍SpringCloudLoadBalancer如何自定义负载均衡策略。        SpringCloudLoadBalancer的默认负载均衡策略为轮询策略。我们可......
  • Atomsk孪晶多晶建模方法
    大家好,我是小马老师。本文介绍atomsk孪晶多晶建模方法。atomsk多晶建模的原理是先建立一个晶胞,然后编写一个polycrystal.txt文件,设定最终模型的尺寸和晶粒的个数。atomsk按......
  • JavaScript之字符串的方法
    JavaScript字符串用于存储和处理文本。String(字符串)可以存储一系列字符,如"JavaScript之字符串的方法"。String也是基础数据类型。接下来查看String常用方法,如有不足和......
  • 集合是否存在交集的判断方法分享
    转自:http://www.java265.com/JavaJingYan/202206/16554733883740.html集合:  集合,简称集,是数学中一个基本概念,也是集合论的主要研究对象。集合论的基本理论创立于19世......