JavaWeb12-线程通讯(线程等待和唤醒)

news2024/11/29 13:32:37

目录

1.方法介绍

1.1.wait()/wait(long timeout):让当前线程进入等待状态。

1.1.1.wait执行流程

1.1.2.wait结束等待的条件

1.1.3.wait() VS wait(long timeout)

1.1.4.为什么wait要放在Object中?

--->PS:wait(0) 和 sleep(0) 的区别

--->PS:wait 和 sleep 释放锁行为的区别

--->PS:(常见面试题)wait 和 sleep 的区别(小结)

--->PS:3种方法让线程进入休眠/等待状态

1.2.notify():唤醒当前对象上一个休眠的线程(随机)。

1.3.notifyAll():唤醒当前对象上的所有线程。

2.注意事项:


由于线程之间是抢占式执⾏的,因此线程之间执⾏的先后顺序难以预知。但是实际开发中有时候希望合理地协调多个线程之间的执⾏先后顺序。

球场上的每个运动员都是独⽴的 "执⾏流",可以认为是⼀个 "线程"。⽽完成⼀个具体的进攻得分动作,则需要多个运动员相互配合, 按照⼀定的顺序执⾏⼀定的动作,线程1 先 "传球",线程2 才能 "扣篮"。

1.方法介绍

完成这个协调⼯作(线程通讯),主要涉及到以下三个⽅法。注意这三个方法都是对象级别的(需要通过"对象."来调用),【不是锁级别,如Thread.sleep()】都是Object类的内置方法。

1.1.wait()/wait(long timeout):让当前线程进入等待状态。

1.1.1.wait执行流程

  1. 使当前执⾏代码的线程进⾏等待。(把线程放到等待队列中)。
  2. 释放当前的锁。
  3. 满⾜⼀定条件时被唤醒, 重新尝试获取这个锁。

1.1.2.wait结束等待的条件

  1. 其他线程调⽤该对象的 notify ⽅法。wait/notify 唤醒顺序是无序的。
  2. wait 等待时间超时 (wait ⽅法提供⼀个带有 timeout 参数的版本, 来指定等待时间)。
  3. 其他线程调⽤该等待线程的 interrupted ⽅法, 导致 wait 抛出 InterruptedException 异常。

/**
 * wait使用
 */
public class WaitDemo {
    public static void main(String[] args) {
        Object lock = new Object();
        Thread t1 = new Thread(() -> {
            System.out.println("线程1开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程1调用wait方法....");
                    //无限期等待状态
                    lock.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1执行完成");
        },"线程1");
        t1.start();
    }
}

import java.util.concurrent.TimeUnit;

public class WaitSleepDemo7 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (lock){
                System.out.println("线程1:开始执行");
                try{
                    lock.wait(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1:结束执行");
            }
            System.out.println("线程1:终止执行");
        },"wait0");
        t1.start();

        TimeUnit.SECONDS.sleep(1);
        System.out.println("执行线程1的终止方法");
        t1.interrupt();
    }
}

1.1.3.wait() VS wait(long timeout)

1-不同点:wait(long timeout):当线程超过了设置的时间之后,自动恢复执行;而wait():无限等待状态。(0表示无限等待状态)

2-不同点:wait(long timeout):线程会进入WAITING状态;而wait():线程会进入TIMED_WAITING状态。

import java.time.LocalDateTime;

public class WaitDemo4 {
    public static void main(String[] args) {
        Object lock = new Object();
        Object lock2 = new Object();

        new Thread(() -> {
            System.out.println("线程1:开始执行");
            synchronized (lock){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1:执行完成");
            }
        },"无参wait线程").start();

        new Thread(() -> {
            synchronized (lock2){
                System.out.println("线程2:开始执行 |" + LocalDateTime.now());
                try {
                    lock2.wait(60 * 60 * 60 * 1000); //1h
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程2:执行完成 |" + LocalDateTime.now());
            }
        },"有参wait线程").start();
    }
}

 

3-共同点:无论是wait(long timeout)还是wait(),都会使当前线程进入休眠状态。

4-共同点:无论是wait(long timeout)还是wait(),都能使用notify/notifyAll进行唤醒。

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

public class WaitDemo6 {
    public static void main(String[] args) {
        Object lock = new Object();

        new Thread(() -> {
            System.out.println("线程1:开始执行");
            synchronized (lock){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1:执行完成");
            }
        },"无参wait线程").start();

        new Thread(() -> {
            synchronized (lock){
                System.out.println("线程2:开始执行 |" + LocalDateTime.now());
                try {
                    lock.wait(60 * 60 * 60 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程2:执行完成 |" + LocalDateTime.now());
            }
        },"有参wait线程").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock){
               System.out.println("唤醒所有线程");
               lock.notifyAll();
           }
        }).start();
    }
}

1.1.4.为什么wait要放在Object中?

wait 使用要加锁,也就是要操作锁,锁是针对对象级别的而非线程级别的,线程和对象是⼀对多,所以 wait 最便利的方式是放在 Object 中。

wait(num)和sleep(num):当num>0时,二者执行效果一样。

--->PS:wait(0) 和 sleep(0) 的区别

  1. wait(0) 表示无期限地等待,直到有线程唤醒它为止。
  2. Thread.sleep(0) 调用后会让出CPU执行权,让线程稍做休眠,然后重新调度,重新触发一次 CPU 竞争,不管是否竞争到CPU执行权,都会继续执行,直到执行结束。(类似于Thread.yield())
/**
 * wait(0)和sleep(0)
 */
public class WaitSleepDemo7 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (lock){
                System.out.println("线程1:开始执行");
                try{
                    lock.wait(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1:执行结束");
            }
        },"wait(0)");
        t1.start();

        Thread t2 = new Thread(() -> {
            System.out.println("线程2:开始执行");
            try {
                Thread.sleep(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2:执行结束");
        },"sleep(0)");
        t2.start();
    }
}

--->PS:wait 和 sleep 释放锁行为的区别

wait和sleep在有锁的情况下,锁的处理行为是完全不同的:

  1. wait方法(不管是有参还是无参)在执行时都会释放锁。
  2. sleep方法不会释放锁。
import java.util.concurrent.TimeUnit;

/**
 * wait和sleep释放锁行为的区别
 */
public class WaitSleepDemo8 {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Object lock2 = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (lock){
                System.out.println("线程1:开始执行");
                try{
                    lock.wait(3 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1:结束执行");
            }
        },"wait");
        t1.start();

        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("线程2:开始执行");
                try {
                    Thread.sleep(3 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程2:结束执行");
            }
        },"sleep");
        t2.start();

        //创建2个线程,先让线程休眠1秒之后,尝试获取锁,看能不能获取到锁
        //如果可以获取到锁,说明休眠时线程是释放锁的,而如果获取不到锁,说明是不释放锁的
        Thread t3 = new Thread(() -> {
            try{
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("尝试获取wait方法的锁");
            synchronized (lock){
                System.out.println("成功获取wait的锁");
            }
        },"wait2");
        t3.start();

        Thread t4 = new Thread(() -> {
            try{
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("尝试获取sleep方法的锁");
            synchronized (lock2){
                System.out.println("成功获取sleep的锁");
            }
        },"sleep2");
        t4.start();
    }
}

为什么wait释放锁而sleep不释放锁?

JVM 强制语法检查,wait 方法默认等待无期限。

--->PS:(常见面试题)wait 和 sleep 的区别(小结)

二者的相同点:

  1. 都可以让线程休眠。
  2. 都可以响应 interrupt 中断的请求。
  3. wait(num) 和 sleep(num):当num>0时,二者执行效果一样。

二者的不同点:

  1. wait 必须在 synchronized 中使用;而 sleep 却不用。
  2. wait 是 Object 的方法(属于对象级别);而sleep 是 Thread 的方法(属于线程级别)。
  3. wait 释放锁;而sleep 不释放锁。
  4. wait 有可能无限期地等待下去;而sleep 有明确的终止等待时间。 即:wait 可传参,也可不传参;而 sleep必须要传递一个数值类型的参数,否则会报错。
  5. wait 和 sleep 产生的线程状态是不同的:无参的 wait 是 WAITING 状态;而sleep 是 TIMED_WAITING 状态。
  6. 一般情况下(响应 interrupt 除过),wait 可以接收一个 notify/notifyAll 之后就继续执行;而 sleep 只能等待超过时间之后再恢复执行。
  7. wait(0) 表示无期限地等待;而sleep(0)表示重新触发一次 CPU 竞争。

--->PS:3种方法让线程进入休眠/等待状态

  1. sleep(传参设置休眠时间;不可唤醒)
  2. TimeUnit(传参设置休眠时间;不可唤醒)
  3. wait(可传参设置休眠时间,也可不传参无限等待;可以唤醒)

1.2.notify():唤醒当前对象上一个休眠的线程(随机)。

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

  • 如果有多个线程等待,则由线程调度器随机挑选出⼀个呈 wait 状态的线程。(并没有 "先来后到") (官方是随机的,但具体实现针对不同的JVM是不一样的)

/**
 * notify使用
 */
public class WaitDemo2 {
    public static void main(String[] args) {
        Object lock = new Object();
        Object lock2 = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println("线程1开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程1调用wait方法....");
                    //无限期的等待状态
                    lock.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程1执行完成");
        },"线程1");

        Thread t2 = new Thread(() -> {
            System.out.println("线程2开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程2调用wait方法....");
                    //无限期的等待状态
                    lock.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程2执行完成");
        },"线程2");

        Thread t3 = new Thread(() -> {
            System.out.println("线程3开始执行");
            try {
                synchronized (lock2) {
                    System.out.println("线程3调用wait方法....");
                    //无限期的等待状态
                    lock2.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程3执行完成");
        },"线程3");

        t1.start();
        t2.start();
        t3.start();

        //唤醒lock对象上休眠的线程(随机唤醒一个)
        Thread t4 = new Thread(() -> {
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
            }
            System.out.println("线程4:开始执行,唤醒线程");
            synchronized (lock) {
                //发出唤醒通知
                lock.notify();
                System.out.println("线程4:执行了唤醒操作");
                try{
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
                System.out.println("线程4:synchronized执行完了");
            }
        }, "线程4");
        t4.start();
    }
}

1.3.notifyAll():唤醒当前对象上的所有线程。

/**
 * notifyAll使用
 */
public class WaitDemo3 {
    public static void main(String[] args) {
        Object lock = new Object();
        Object lock2 = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println("线程1开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程1调用wait方法....");
                    //无限期的等待状态
                    lock.wait();
                    System.out.println("线程1:恢复执行之后又进入休眠状态");
                    Thread.sleep(2000);
                    System.out.println("线程1执行完成");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程1");

        Thread t2 = new Thread(() -> {
            System.out.println("线程2开始执行");
            try {
                synchronized (lock2) {
                    System.out.println("线程2调用wait方法....");
                    //无限期的等待状态
                    lock2.wait();
                    System.out.println("线程2:恢复执行之后又进入休眠状态");
                    Thread.sleep(2000);
                    System.out.println("线程2执行完成");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程2");

        Thread t3 = new Thread(() -> {
            System.out.println("线程3开始执行");
            try {
                synchronized (lock) {
                    System.out.println("线程3调用wait方法....");
                    //无限期的等待状态
                    lock.wait();
                    System.out.println("线程3:恢复执行之后又进入休眠状态");
                    Thread.sleep(2000);
                    System.out.println("线程3执行完成");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程3");

        t1.start();
        t2.start();
        t3.start();

        //唤醒lock对象上休眠的线程(随机唤醒一个)
        Thread t4 = new Thread(() -> {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
            System.out.println("线程4:开始执行,唤醒线程");
            synchronized (lock){
                //发出唤醒通知
                lock.notifyAll();
                System.out.println("线程4:执行了唤醒操作");
                try{
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }
                System.out.println("线程4:synchronized执行完了");
            }
        }, "线程4");
        t4.start();
    }
}

2.注意事项:

①wait/notify/notifyAll必须要配合synchronized一起使用,否则会报错(JVM的强制规定,之所以这样规定,是为了解决线程通讯时执行混乱的问题,synchronized起到约束限制的作用)。

②wait/notify/notifyAll进行synchronized加锁,一定要使用同一个对象进行加锁。

③当调用了notify/notifyAll之后,当前线程不会⻢上释放该对象锁,要等到执⾏notify/notifyAll⽅法的线程将程序执⾏完,也就是退出同步代码块(synchronized)之后才会释放对象锁。【程序也并不会立即恢复执行,而是尝试获取锁,只有得到锁之后才能继续执行。】

④notify/notifyAll可以多次调用,也可以在wait之前调用(是无用功)。

⑤notifyAll 并不是唤醒所有 wait 等待的线程,而是唤醒当前对象处于 wait 等待的所有线程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/363843.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法训练营 day52 动态规划 买卖股票的最佳时机系列1

算法训练营 day52 动态规划 买卖股票的最佳时机系列1 买卖股票的最佳时机 121. 买卖股票的最佳时机 - 力扣(LeetCode) 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票…

VR全景多种玩法打破传统宣传,打造全新云端视界

传统的展示方式只是在进行单方面的表达,不论是图片、视频,都无法让浏览者有参与感,这样的展示宣传效果自然比不上VR全景展示,VR全景基于真实场景来形成三维图像,其沉浸式和无视野盲区的特点让用户更有真实感和沉浸感&a…

python -- 魔术方法

魔术方法就算定义在类里面的一些特殊的方法 特点:这些func的名字前面都有两个下划线 __new__方法 相当于一个类的创建一个对象的过程 __init__方法 相当于为这个类创建好的对象分配地址初始化的过程 __del__方法 一个类声明这个方法后,创建的对象如果…

九龙证券|创业板向未盈利企业敞开大门 考验投行估值定价能力

未盈余企业上市有了新选择。2月17日,全面实行股票发行注册制相关准则规矩发布施行。深交所发布《深圳证券交易所创业板股票上市规矩(2023年修订)》及《关于未盈余企业在创业板上市相关事宜的告诉》,“预计市值不低于50亿元&#x…

设计模式C++实现23:中介者模式(Mediator)

部分内容参考大话设计模式第25章;本实验通过C语言实现。 一 原理 意图:用一个中介对象来封装一系列对象的交互,中介者使得各个对象不需要显示地相互引用,从而使耦合松散,而且可以独立地改变它们之间的交互。 上下文…

OCR项目实战(一):手写汉语拼音识别(Pytorch版)

👨‍💻作者简介: 大数据专业硕士在读,CSDN人工智能领域博客专家,阿里云专家博主,专注大数据与人工智能知识分享。 🎉专栏推荐: 目前在写一个CV方向专栏,后期会更新不限于…

git merge和git rebase命令

参考链接:https://www.cnblogs.com/michael-xiang/p/13179837.html 学习背景:已经学习过git,但是实践较少,未和他人协作 1.merge git merge表示把当前分支合并到版本库中下拉的远程分支上。 git merge A B表示把A分支合并到B上…

DACS: Domain Adaptation via Cross-domain Mixed Sampling 学习笔记

DACS介绍方法Naive MixingDACSClassMix![在这里插入图片描述](https://img-blog.csdnimg.cn/ca4f83a2711e49f3b754ca90d774cd50.png)算法流程实验结果反思介绍 近年来,基于卷积神经网络的语义分割模型在众多应用中表现出了显著的性能。然而当应用于新的领域时&…

2250216-92-1,Propargyl-PEG3-triethoxysilane,炔基-三聚乙二醇-三乙氧基硅烷,具有高效稳定和特异性

【中文名称】炔基-三聚乙二醇-三乙氧基硅烷【英文名称】 Propargyl-PEG3-triethoxysilane【结 构 式】【CAS号】2250216-92-1【分子式】C19H37NO7Si【分子量】419.59【基团部分】炔基基团【纯度标准】95%【包装规格】1g,5g,10g,可以提供核磁图…

Zebec生态持续深度布局,ZBC通证月内翻倍或只是开始

“Zebec生态近日利好不断,除了推出了回购计划外,Nautilus Chain、Zebec Labs等也即将面向市场,都将为ZBC通证深度赋能。而ZBC通证涨幅月内突破100%,或许只是开始。”近日,流支付生态Zebec生态通证ZBC迎来了大涨&#x…

计算机网络的166个核心概念,你知道吗?

上回我整理了一下计算机网络中所有的关键概念,很多小伙伴觉得很有帮助,但是有一个需要优化的点就是这些概念不知道出自哪里,所以理解起来像是在云里穿梭,一会儿在聊应用层的概念,一会儿又跑到网络层协议了。针对这种情…

小学生学Arduino---------点阵(二)动态图片以及文字

今天进阶了利用人眼视觉暂留原理制作动态的图片变换。 1、熟练掌握图片显示器的使用 2、创作多种动态图片、文字的显示 3、明确动态图片、文字显示过程 4、掌握图片显示器中清空指令的使用 5、搭建动态图片、文字的显示电路 6、编写动态图片、文字的程序 复习: 绘…

@Slf4j注解的使用

1.Slf4j的作用? 很简单的就是为了能够少写两行代码,不用每次都在类的最前边写上: private static final Logger logger LoggerFactory.getLogger(this.XXX.class); 我们只需要在类前面添加注解Slf4j,即可使用log日志的功能了 2.…

美格智能与宏电股份签署战略合作协议,共创5G+AIoT行业先锋

2月17日,美格智能技术股份有限公司CEO杜国彬及相关业务负责人员一行到访深圳市宏电技术股份有限公司总部大厦参观交流,并参加了主题为“聚势同行、合创未来”宏电股份与美格智能2023年IoT产业生态合作研讨会,受到了宏电股份总裁张振宇及相关业…

手写Promise方法(直击Promise A+规范)

前言:大家好,我是前端獭子。高效优雅是我的创作方式。学习是一个渐进的过程,要把知识串联起来才能解决某一方面的问题。 Promise 构造函数 我们先来写 Promise 构造函数的属性和值,以及处理new Promise()时会传入的两个回调函数。…

【Git】IDEA集合Git和码云

目录 7、IDEA集合Git 7.1 配置Git忽略文件-IDEA特定文件 7.2 定位 Git 程序 7.3 初始化本地库 7.4 添加到暂存区 7.5 提交到本地库 7.6 切换版本 7.7 创建分支 7.8 切换分支 7.9 合并分支 7.10 解决冲突 8、 Idea集成码云 8.1 IDEA 安装码云插件 8.2 分析工程到码…

QHashIterator-官翻

QHashIterator Class template <typename Key, typename T> class QHashIterator QHashIterator 类为 QHash 和 QMultiHash 提供 Java 风格的常量迭代器。更多内容… 头文件:#include qmake:QT core 所有成员列表&#xff0c;包括继承的成员废弃的成员 公共成员函数…

硫酸锂溶液除钙镁树脂系统

H-93锂盐净化除钙镁镁螯合树脂是包含氨甲膦酸基连接到聚苯乙烯共聚物的一种耐用的大孔树脂。 CH-93是用于锂盐净化除钙镁从含有一价阳离子的废水处理中选择性的除去二价金属阳离子。使二价金属阳离子以及由其他二价阳离子可以像钙一样容易地从一价阳离子中分离出来。 CH-93是…

[论文阅读笔记19]SiamMOT: Siamese Multi-Object Tracking

这是CVPR2021的一篇文章, 是利用SOT的一些思想来进行MOT的运动估计. 文章地址: 文章 代码地址: 代码 0. 摘要 本文提出了一个孪生(Siamese)式的MOT网络, 该网络用来估计帧间目标的运动. 为了探究运动估计对多目标跟踪的影响, 本文提出了两种运动建模方式: 显式和隐式. 本文在…

C++(41)-低版本升级到VS2019项目时遇到的问题(2)

1.错误码&#xff1a;MSB8066 代码为3 QT 项目老版本升级到新版本造成的&#xff0c; 1.重新加载项目&#xff1a; 扩展->QT VS tools->Open QT project files-> 2.添加QT模块&#xff1a;QT Project-Settings -> QT Modules2.无法打开QT的头文件 3.…