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

news2024/12/27 10:45:03

目录

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,认为还有体力可以继续执行。

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

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

相关文章

解决vue3项目vite打包忽略.vue扩展名

项目打包时报could not relolve “...”&#xff0c;因为vite已不再默认忽略.vue扩展名。 解决方法如下&#xff1a; 在vite.config.js中配置vite使其忽略 .vue 扩展名&#xff08;不建议忽略&#xff09; 注意&#xff1a;即使忽略了.vue文件&#xff0c;在实际写的时候也要加…

【Linux】为 VMware 的 Linux 系统(CentOS 7)设置静态IP地址

文章目录 准备工作查看 子网掩码 和 网关IP确认准备设置的虚拟机端口没有被占用 调整设置编辑配置文件配置文件说明 完成配置&#xff0c;准备测试使用命令终端连接服务器 我是一名立志把细节说清楚的博主&#xff0c;欢迎【关注】&#x1f389; ~ 原创不易&#xff0c; 如果有…

OS复习笔记ch6-1

死锁的原理 定义 一组进程中&#xff0c;其中每个进程因等待事件而阻塞&#xff0c;且所等待的事件只能被这组进程中的另一阻塞进程激发称之为死锁。 举例如下 四个车辆希望紧迫的希望能很快通过&#xff0c;每辆车需要两个象限的资源&#xff0c;然而四个车都只得到一个象…

使用 Django Rest Framework 构建强大的 Web API

文章目录 安装 Django Rest Framework创建序列化器创建视图和 URL 路由配置认证和权限测试 API Django Rest Framework&#xff08;DRF&#xff09;是一个强大的工具&#xff0c;用于在 Django Web 框架中构建灵活且功能丰富的 Web API。它提供了许多功能&#xff0c;包括序列化…

揭秘Kafka从入门到精通,架构最全详解

Kafka架构最全详解 Kafka&#xff0c;作为关键消息中间件&#xff0c;广泛应用于大型架构与顶尖企业。本篇深入解析Kafka架构&#xff0c;掌握其核心技术要点。 Kafka Apache Kafka 是一个分布式发布-订阅消息系统&#xff0c;由LinkedIn开创的分布式发布-订阅消息系统&#x…

长安链使用Golang编写智能合约教程(一)

编写前的注意事项&#xff1a; 1、运行一条带有Doker_GoVM的链 2、建议直接用官方的在线IDE去写合约&#xff0c;因为写完可以直接测&#xff0c;缺点只是调试不方便。 3、自己拉环境在本地写合约&#xff0c;编译时注意编译环境&#xff0c;官方有提醒你去Linux下去编译。 …

010-Linux磁盘介绍

文章目录 1、名词 2、类型 3、尺寸 4、接口/协议/总线 5、命名 6、分区方式 MBR分区 GPT分区 1、名词 磁盘是计算机主要的存储介质&#xff0c;可以存储大量的二进制数据&#xff0c;并且断电后也能保持数据不丢失。早期计算机使用的磁盘是软磁盘&#xff08;Floppy D…

牛客网刷题 | BC99 正方形图案

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

CST初级教程 六

本篇教程将以差分线为例&#xff0c;实例讲解参数化建模及参数扫描。 一 Project创建 点击New and Recent&#xff0c;再点击New Template 点击MICROVAVES & RF/OTICAL&#xff0c;然后在选中Circuit & Components。 点击对话框中Next按钮&#xff0c;在弹出对话框…

C语言中的七种常用排序

今天&#xff0c;为大家整理了C语言中几种常用的排序&#xff0c;以及他们在实际中的运用&#xff08;有Bug请在下方评论&#xff09;&#xff1a; 一.桶排序 #include <stdio.h> int main() {int book[1001],i,j,t,n;for(i0;i<1000;i)book[i]0;scanf("%d"…

B树与B+树区别

B树和B树是常见的数据库索引结构&#xff0c;都具有相较于二叉树层级较少&#xff0c;查找效率高的特点&#xff0c;它们之间有以下几个主要区别&#xff1a; 1.节点存储数据的方式不同 B树的叶子结点和非叶子节点都会存储数据&#xff0c;指针和数据共同保存在同一节点中B树…

MySQL的索引, 到底怎么创建?

目录 前言 MySQL的数据结构 索引是一把双刃剑 索引创建原则 如何给一个列挑选索引? 索引列的基数, 要尽量小 索引列的类型尽量小 索引长字符串的前缀 不要对索引列进行计算操作或者函数计算. 不要老想着查询, 想想插入该怎么办? 避免索引冗余和重复 前言 今天在…

【二叉树】:LeetCode:100.相同的数(分治)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;初阶初阶结构刷题 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 1.问题描述&#xff1a; 2.问题分析&#xff1a; 二叉树是区分结构的&#xff0c;即左右子树是不一…

上5个B端系统的设计规范,让你的开发比着葫芦画瓢。

B端系统设计规范在企业级系统开发中起着重要的作用&#xff0c;具体包括以下几个方面&#xff1a; 统一风格和布局&#xff1a;设计规范能够统一系统的风格和布局&#xff0c;使不同功能模块的界面看起来一致&#xff0c;提升用户的使用体验和学习成本。通过统一的设计规范&am…

模型驱动架构设计方法及应用

引言 模型驱动架构&#xff08;Model Driven Architecture&#xff0c;MDA&#xff09;是一种软件开发方法论&#xff0c;它强调使用一系列抽象层次的模型&#xff0c;并利用模型之间的转换来实现从需求到设计、直至代码生成的全过程。MDA的核心思想是在软件开发过程中强调使用…

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…

CentOS 7.9部署宝塔面板超详细

CentOS7 部署宝塔面板 Linux的宝塔面板搭建起来非常轻松&#xff0c;也可以用一句话来形容&#xff0c;如果喝水一样简单&#xff0c;只需一条命令剩下的交给时间&#xff0c;几分钟就能部署好&#xff0c;然后就可以直接进行登录&#xff0c;直接可以安装LNMP、LAMP平台&…

数据结构与算法(Java版) | 详解十大经典排序算法之一:希尔排序

接下来&#xff0c;我来给大家讲解第四种排序算法&#xff0c;即希尔排序。 简单插入排序所存在的问题 在上篇文章中&#xff0c;我已经给大家讲解完插入排序了&#xff0c;虽说是讲完了&#xff0c;但在这里我还是想请大家开动脑筋思考一下&#xff0c;就是咱们讲解的插入排…

SDK崩溃后怎么打开已有工程

1.进到SDK里面&#xff0c;保留&#xff1a;platform、bsp和工程包&#xff08;这里是C&#xff09;&#xff0c;其他都删掉 2.windows窗口运行sdk 3.导入sdk工程

基于springboot+vue的4S店车辆管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…