山东大学软件学院项目实训-创新实训-基于大模型的旅游平台(二十)- JUC(6)

目录

wait , notify

wait vs sleep

正确使用方法

同步保护性暂停

join的源码

Future

异步生产者/消费者模型

定义

Park & Unpark

原理


wait , notify

小故事小南需要烟才能工作,但它又要占这锁让别人无法进来。那么这个时候开一个waitSet相当于就是休息室让小南进去。并且释放锁。如果烟到了,那么notify小南就能够继续工作了。

Blocked和Waiting区别其实就是waiting是释放了锁,blocked是没有锁waiting被notify之后仍然需要进入到entrylist进行等待。

  
  @Slf4j(topic = "c.TestWaitNotify")
  public class Test {
  ​
      // 锁对象
      final static Object obj = new Object();
  ​
      public static void main(String[] args) throws InterruptedException {
  ​
          new Thread(() -> {
              synchronized (obj) {
                  log.debug("执行....");
                  try {
                      obj.wait(); // 让线程在obj上一直等待下去
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  log.debug("其它代码....");
              }
          },"t1").start();
  ​
          new Thread(() -> {
              synchronized (obj) {
                  log.debug("执行....");
                  try {
                      obj.wait(); // 让线程在obj上一直等待下去
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  log.debug("其它代码....");
              }
          },"t2").start();
  ​
          // 主线程两秒后执行
          Thread.sleep(5000);
          log.debug("唤醒 obj 上其它线程");
          synchronized (obj) {
  //            obj.notify(); // 唤醒obj上一个线程
              obj.notifyAll(); // 唤醒obj上所有等待线程
          }
      }
  }
  
  20:17:53.579 [t1] DEBUG c.TestWaitNotify - 执行....
  20:17:53.581 [t2] DEBUG c.TestWaitNotify - 执行....
  20:17:58.584 [main] DEBUG c.TestWaitNotify - 唤醒 obj 上其它线程
  20:17:58.584 [t2] DEBUG c.TestWaitNotify - 其它代码....
  20:17:58.584 [t1] DEBUG c.TestWaitNotify - 其它代码....
  ​
  进程已结束,退出代码0

wait vs sleep

sleep:Thread调用,静态方法,而且不会释放锁

wait:所有obj,但是要配合synchronize使用,可以释放锁

sleep在睡眠时,不会释放锁,wait会释放对象锁

通常锁会加上final防止被修改

正确使用方法

小南需要烟才能工作,如果是使用sleep不释放锁,那么其他需要等待干活的人就会干等着,等烟来。但是wait可以让小南释放锁,让其他线程工作,并且唤醒小南

  
  @Slf4j(topic = "c.TestCorrectPosture")
  public class Test {
      static final Object room = new Object();
  ​
      // 有无烟
      static boolean hasCigarette = false;
      static boolean hasTakeout = false;
  ​
      public static void main(String[] args) throws InterruptedException {
          new Thread(() -> {
              synchronized (room) {
                  log.debug("有烟没?[{}]", hasCigarette);
                  if (!hasCigarette) {
                      log.debug("没烟,先歇会!");
                      try {
                          Thread.sleep(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();
          }
  ​
          Thread.sleep(1000);
          // 送烟线程
          new Thread(() -> {
              synchronized (room) {
                  hasCigarette = true;
                  log.debug("烟到了噢!");
              }
          }, "送烟的").start();
      }
  ​
  }
  
  20:32:22.014 [小南] DEBUG c.TestCorrectPosture - 有烟没?[false]
  20:32:22.019 [小南] DEBUG c.TestCorrectPosture - 没烟,先歇会!
  20:32:24.024 [小南] DEBUG c.TestCorrectPosture - 有烟没?[false]
  20:32:24.024 [送烟的] DEBUG c.TestCorrectPosture - 烟到了噢!
  20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以开始干活了
  20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以开始干活了
  20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以开始干活了
  20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以开始干活了
  20:32:24.024 [其它人] DEBUG c.TestCorrectPosture - 可以开始干活了
  ​
  进程已结束,退出代码0

存在的问题 :

  1. 其它干活的线程,都要一致阻塞,效率低

  2. 就算烟提前送到,也无法立刻醒来

  3. 送烟加上锁之后,相当于门一直锁着,烟送不进去

改进 :

  
  @Slf4j(topic = "c.TestCorrectPosture")
  public class Test {
      static final Object room = new Object();
  ​
      // 有无烟
      static boolean hasCigarette = false;
      static boolean hasTakeout = false;
  ​
      public static void main(String[] args) throws InterruptedException {
          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("可以开始干活了");
                  }
              }
          }, "小南").start();
  ​
          for (int i = 0; i < 5; i++) {
              new Thread(() -> {
                  synchronized (room) {
                      log.debug("可以开始干活了");
                  }
              }, "其它人").start();
          }
  ​
          Thread.sleep(1000);
          // 送烟线程
          new Thread(() -> {
              synchronized (room) {
                  hasCigarette = true;
                  log.debug("烟到了噢!");
                  room.notify();    // 叫醒小南线程
              }
          }, "送烟的").start();
      }
  ​
  }

存在问题

会不会有其他线程在等待着锁?如果是那么会不会唤醒错了线程?(虚假唤醒)

解决 :

可以通过while多次判断条件是否成立,直接使用notifyAll来唤醒所有的线程。然后线程被唤醒之后先再次判断条件是否成立,成立那么往下面执行,如果不成立那么继续执行wait。

  
                  while (!hasCigarette) {
                      log.debug("没烟,先歇会!");
                      try {
                          room.wait();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }

正确使用 :

  
   synchronized(lock){
      while(条件不成立){
          lock.wait();
      }   
     // 干活
   }
  ​
      // 另一个线程
   synchronized(lock){
       lock.notifyAll();
   }

同步保护性暂停

定义

  • t1需要t2的结果,那么就可以通过一个中间对象guardedObject来充当这个中间商,t2执行完就发送消息到obj,然后obj交给t1

  • 如果是不断发送结果那么可以使用消息队列

  • 要等待所以是同步

  • join和future就是用的这个原理

  
  public class Test {
      public static void main(String[] args) {
          GuaObj guaObj = new GuaObj();
          Thread thread = new Thread(() -> {
              System.out.println("锁住,等待结果");
              guaObj.get(2000);
              System.out.println("解锁");
          }, "t1");
          thread.start();
  ​
  ​
          Thread thread1 = new Thread(() -> {
              System.out.println("先睡两秒");
              try {
                  Thread.sleep(2000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("解锁,设置对象");
              guaObj.set(new Object());
          }, "t2");
          thread1.start();
      }
  }
  ​
  class GuaObj{
      // 结果
      public Object response;
  ​
      // 获取结果
      // timeout表示最多等多久
      public Object get(long timeout){
          synchronized (this){
              // 开始时间
              long cur = System.currentTimeMillis();
              // 经历的时间
              long paseTime=0;
              while(response==null){
                  try {
                    // 这一轮应该等的时间
                      long waitTime=timeout-paseTime;
                      //超时就不等了
                      if(waitTime<=0) break;
                      this.wait(waitTime);
                      paseTime=System.currentTimeMillis()-cur;
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              System.out.println("等待结束");
              return response;
          }
      }
  ​
      // 产生结果
      public void set(Object response){
          synchronized (this){
              this.response=response;
              this.notifyAll();
          }
      }
  }
  
  锁住,等待结果
  先睡两秒
  解锁,设置对象
  等待结束
  解锁
  ​
  进程已结束,退出代码0
  • 需要记录超时的时间,并且重新设置waittime,原因是可能会有虚假唤醒,那么这个时候超时时间不是timeout而是timeout-passedTime,也就是线程执行的时间。

  • 如果超时的话,那么就会自动结束

join的源码

  
   public final synchronized void join(long millis)
      throws InterruptedException {
        //一开始的时间
          long base = System.currentTimeMillis();
       //线程执行的时间
          long now = 0;
  ​
       //如果是<0那么就抛出异常
          if (millis < 0) {
              throw new IllegalArgumentException("timeout value is negative");
          }
  ​
       //如果是0那么就一直等待线程执行完,isAlive是否生存
          if (millis == 0) {
              while (isAlive()) {
                  wait(0);
              }
          } else {
              //timeout超时那么就结束
              while (isAlive()) {
                  long delay = millis - now;
                  if (delay <= 0) {
                      break;
                  }
                  wait(delay);
                  now = System.currentTimeMillis() - base;
              }
          }
      }

Future

相当于就是一个信箱,里面装了很多GuardObject对象,线程可以通过对应的地址访问对象获取结果

异步生产者/消费者模型

定义

相当于就是生产者给队列生产结果,消费者负责处理结果

  • 不需要一一对应

  • 平衡资源

  • 消息队列有容量控制

  • 阻塞队列控制结果出队列

  
  public class Test {
      public static void main(String[] args) {
          MesageQueue queue = new MesageQueue(2);
  ​
          for (int i = 0; i < 3; i++) {
              int id = i;
              new Thread(() -> {
                  queue.set(new Message(id, "值" + id));
              },"生产者" + i).start();
          }
  ​
          new Thread(() -> {
              while(true){
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
                  Message message = queue.take();
              }
          }, "消费者").start();
      }
  }
  ​
  ​
  @Slf4j
  class MesageQueue{
      //存消息的集合
      private LinkedList<Message> list = new LinkedList();
      // 消息容量
      private int capacity;
  ​
      public MesageQueue(int capacity){
          this.capacity = capacity;
      }
  ​
      // 获取消息
      public  Message take()  {
          // 检查队列是否为空
          synchronized (list){
              while(list.isEmpty()){
                  try {
                      log.debug("队列为空,消费者线程等待");
                      list.wait();
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
              }
              Message message = list.removeFirst();
              log.debug("已经消费了消息 {}",message);
              list.notifyAll();
              return message;
          }
      }
  ​
      // 存入消息
      public void set(Message message) {
          // 检查是不是满了
          synchronized (list){
              while(list.size() == capacity){
                  try {
                      log.debug("队列已满,生产者线程等待");
                      list.wait();
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
              }
              list.addLast(message);
              log.debug("已经生产了消息 {}",message);
              list.notifyAll();
          }
      }
  }
  ​
  // 消息类
  final class  Message{
      private int id;
      private Object value;
  ​
      public Message(int id, Object value){
          this.id = id;
          this.value = value;
      }
  ​
      public int getId() {
          return id;
      }
  ​
      public Object getValue() {
          return value;
      }
  ​
      @Override
      public String toString() {
          return "Message{" +
                  "id=" + id +
                  ", value=" + value +
                  '}';
      }
  }
  
  12:58:24.373 [生产者1] DEBUG MesageQueue - 已经生产了消息 Message{id=1, value=值1}
  12:58:24.375 [生产者2] DEBUG MesageQueue - 已经生产了消息 Message{id=2, value=值2}
  12:58:24.377 [生产者0] DEBUG MesageQueue - 队列已满,生产者线程等待
  12:58:25.371 [消费者] DEBUG MesageQueue - 已经消费了消息 Message{id=1, value=值1}
  12:58:25.371 [生产者0] DEBUG MesageQueue - 已经生产了消息 Message{id=0, value=值0}
  12:58:26.386 [消费者] DEBUG MesageQueue - 已经消费了消息 Message{id=2, value=值2}
  12:58:27.397 [消费者] DEBUG MesageQueue - 已经消费了消息 Message{id=0, value=值0}
  12:58:28.405 [消费者] DEBUG MesageQueue - 队列为空,消费者线程等待

Park & Unpark

与wait和notify的区别

  • 不需要与monitor一起使用

  • 可以精准唤醒和阻塞线程

  • 可以先unpark,但是不能先notify。但是unpark之后park不起作用。

原理

①park,先去到counter里面判断是不是0,如果是那么就让线程进入队列。接着就是把counter设置为0

②unpark,那么唤醒线程,恢复运行,并且把counter设置为1

③先unpark后park,那么就unpark补充counter为1,那么park判断counter是1,认为还有体力可以继续执行。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

druid 1.2.14,application.yaml配置文件中,如何进行数据库加密配置

步骤一&#xff1a;先生成加密的密码&#xff1a; 步骤二&#xff1a;配置application.yaml文件&#xff1a; spring:datasource:driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:username: rootpassword: aPJ35saFz6ASmnmNt…

ASP.NET MVC 快速入门(图文版)

今年是2024年了&#xff0c;没有多少人在ASP.NET 去做开发&#xff0c;都使用ABP框架 &#xff0c;不过我们仍然需要了解ASP.NET MVC 的一个开发流程 MVC概述 MVC是当前比较流行的WEB程序开发模式之一&#xff0c;ASP.NET MVC是.Net对MVC的一种实现。MVC&#xff08;Model View…

LeetCode 124 —— 二叉树中的最大路径和

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 二叉树的问题首先我们要想想是否能用递归来解决&#xff0c;本题也不例外&#xff0c;而递归的关键是找到子问题。 我们首先来看看一棵最简单的树&#xff0c;也就是示例 1。这样的一棵树总共有六条路径&#xf…

Vita-CLIP: Video and text adaptive CLIP via Multimodal Prompting

标题&#xff1a;Vita-CLIP: 通过多模态提示进行视频和文本自适应CLIP 源文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2023/papers/Wasim_Vita-CLIP_Video_and_Text_Adaptive_CLIP_via_Multimodal_Prompting_CVPR_2023_paper.pdfhttps://openaccess.thecvf.…

Java中IO流类的体系

Java为我们提供了多种多样的IO流&#xff0c;我们可以根据不同的功能及性能要求挑选合适的IO流&#xff0c;如图所示&#xff0c;为Java中IO流类的体系。 从上图发现&#xff0c;很多流都是成对出现的&#xff0c;比如&#xff1a; FileInputStream/FileOutputStream&#xff0…

国内首个智能体生态大会!2024百度万象大会定档5月30日

最近&#xff0c;百度悄悄「上新」了几个AI神器。 百度搜索上线「互动」功能&#xff0c;可以实时问答&#xff0c;查询信息就像聊天一样简单&#xff0c;还可以艾特相关智能体&#xff0c;更细致精确地满足个性化需求&#xff0c;比如去新加坡旅游&#xff0c;可以让新加坡旅…

Python - 深度学习系列35 重塑实体识别2

说明 上一篇Python - 深度学习系列34 重塑实体识别介绍了如何进行训练&#xff0c;这篇讨论如何应用。 详细review了之后&#xff0c;发现其实早先的服务还是略有欠缺的。例如&#xff1a; 1 最早的时候好像还没有pipeline&#xff0c;我用DataFrame并行处理&#xff0c;然后…

matlab 图像的中值滤波

目录 一、功能概述1、算法概述2、主要函数3、计算公式二、代码实现三、结果展示四、参考链接本文由CSDN点云侠翻译,放入付费专栏只为防不要脸的爬虫。专栏值钱的不是本文,切勿因本文而订阅。 一、功能概述 1、算法概述 中值滤波是图像处理中一种常用的非线性运算,用于减少…

摸鱼大数据——Hive基础理论知识——Hive环境准备

Hive环境准备 1、shell脚本执行方式 方式1: sh 脚本 注意: 需要进入脚本所在目录,但脚本有没有执行权限不影响执行 方式2: ./脚本 注意: 需要进入脚本所在目录,且脚本必须有执行权限 方式3: /绝对路径/脚本 注意: 不需要进入脚本所在目录,但必须有执行…

Pytorch深度学习实践笔记6(b站刘二大人)

&#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;pytorch深度学习 &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xff1a;学习的本质就是极致重复! 《PyTorch深度学习实践》完结合集_哔哩哔哩_bilibi…

【Spring Security + OAuth2】OAuth2

Spring Security OAuth2 第一章 Spring Security 快速入门 第二章 Spring Security 自定义配置 第三章 Spring Security 前后端分离配置 第四章 Spring Security 身份认证 第五章 Spring Security 授权 第六章 OAuth2 文章目录 Spring Security OAuth21、OAuth2简介1.1、OAu…

绘唐科技绘唐ai工具邀请码

绘唐科技绘唐ai工具邀请码 绘唐AI工具 https://qvfbz6lhqnd.feishu.cn/wiki/QBr4wOAz2ilF4NknrqbcoKRhn2c TensorFlow是一个开源的机器学习框架,由Google开发并维护。它提供了一个灵活且高效的接口,用于构建和训练各种机器学习模型。 TensorFlow的基本概念包括: 1. 张量(…

基于python的网页自动刷新工具

1.下载webdriver https://msedgewebdriverstorage.z22.web.core.windows.net/?prefix122.0.2365.59/下载Edge的浏览器驱动 2.安装selenium pip install selenium4.11.1 3.写代码 # -*- coding: utf-8 -*- import tkinter as tk from tkinter import messagebox import thr…

当标签中出现输入了字母或者数字直接在一行上,没有换行的 情况时怎么办

当标签块中输入的是包含字母或者数字的时候&#xff0c;他不会换行&#xff0c;在一行上显示滚动条的形式&#xff0c;而我们想让他走正常文档流&#xff0c;该换行的时候换行 想要的如下效果 给相应的元素块添加该代码即可 word-break: break-all; .card-content { …

uniapp使用uni.chooseImage选择图片后对其是否符合所需的图片大小和类型进行校验

uni.chooseImage的返回值在H5平台和其他平台的返回值有所差异&#xff0c;具体差异看下图 根据图片可以看出要想判断上传的文件类型是不能直接使用type进行判断的&#xff0c;所以我使用截取字符串的形式来判断&#xff0c;当前上传图片的后缀名是否符合所需要求。 要求&#…

牛客网刷题 | BC97 回文对称数

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 今天牛牛学到了回文…

电子电器架构 - AUTOSAR软件架构介绍

电子电器架构 - AUTOSAR软件架构介绍 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己…

Nginx代理配置(专业版)

写在前面提醒&#xff1a;使用代理&#xff0c;如果可以&#xff0c;请尽量支持双协议&#xff0c;http、https均要支持哈。 注意&#xff1a;监控系统只是运行代码&#xff0c;是否支持https&#xff0c;需要运维同学在你们的服务器上配置https证书&#xff0c;配置好证书&…

关于构建生成式AI产品的思考

在过去的六个月里&#xff0c;我们 LinkedIn 的团队一直在努力开发一种新的人工智能体验。我们希望重新构想我们的会员如何进行求职和浏览专业内容。 生成式人工智能的爆炸式增长让我们停下来思考一年前不可能实现的事情。我们尝试了许多想法&#xff0c;但都没有真正实现&…

OpenAI模型GPT-4o、GPT-4、Gemini 1.5性能比较

大家好&#xff0c;OpenAI最新推出的GPT-4o&#xff0c;标志着人工智能语言模型和交互方式迈入了新纪元。最引人注目的是&#xff0c;GPT-4o支持实时互动和流畅的对话切换&#xff0c;让交流更加自然。 本文将对比分析GPT-4o、GPT 4以及谷歌的Gemini和Unicorn模型&#xff0c;…