JavaEE 【知识改变命运】04 多线程(3)

文章目录

  • 多线程带来的风险-线程安全
    • 线程不安全的举例
    • 分析产出线程安全的原因:
      • 1.线程是抢占式的
      • 2. 多线程修改同一个变量(程序的要求)
      • 3. 原子性
      • 4. 内存可见性
      • 5. 指令重排序
    • 总结线程安全问题产生的原因
    • 解决线程安全问题
      • 1. synchronized关键字的介绍(监视器锁 monitor lock)
        • a.锁的概念
        • b.synchronized的特性
        • c.synchronized的用法
        • d.针对上述问题,加synchronied解决问题,分析底层逻辑
        • e.关于synchronized的总结
        • f.不同锁对象的情况
        • g.如何判断多个线程竞争的是不是同一把锁
        • h.可重入锁
        • I.锁对象
  • Java 标准库中的线程安全类
    • 不安全类
    • 安全类
  • volatile 关键字
    • 解决线程安全的问题
      • 内存可见性
      • 实例
      • CPU层面保证可见性
      • Java层面(保证指令顺序,从而保证内存可见性)
      • 总结
  • wait() 和 notify()
    • wait和notify的基础知识
    • wait()方法
    • notify()⽅法
    • notifyAll()⽅法
    • wait 和 sleep和join的对⽐(⾯试题)
    • wait和notify的总结

多线程带来的风险-线程安全

线程不安全的举例

场景: 用两个线程对同一个变量分别自增5万次,预期结果和自增结果是一个累加和,10万次。

    public static  int count;public static void main(String[] args) {Counter counter = new Counter();Thread t1 =new Thread(()->{for(int i=0;i<5_0000;i++){counter.count();}});Thread t2 =new Thread(()->{for(int i=0;i<5_0000;i++){counter.count();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: "+counter.count);}public  void count(){{count+=1;}} }

执行结果:

在这里插入图片描述
程序运行得到的结果与预期的结果值不一样,而且是一个错误的结果,而且我们程序的逻辑是正确的,这个现象所表现的问题称为线程安全问题

分析产出线程安全的原因:

1.线程是抢占式的

线程是抢占执行的(执行顺序是随机的)
由于线程的执行顺序无法为人控制,抢占式执行是造成线程安全问题的主要罪魁祸首,而且我们解决不了,完全是CPU自己调度,而且和CPU的核数有关

2. 多线程修改同一个变量(程序的要求)

单个线程修改同一个变量不会产生线程安全问题
多个线程修改不同的变量不会产生线程安全问题
多个线程修改同一个变量,会产生线程安全问题

3. 原子性

  • 什么是原⼦性
    我们把⼀段代码想象成⼀个房间,每个线程就是要进⼊这个房间的⼈。如果没有任何机制保证,A进⼊房间之后,还没有出来;B 是不是也可以进⼊房间,打断 A 在房间⾥的隐私。这个就是不具备原⼦性那我们应该如何解决这个问题呢?是不是只要给房间加⼀把锁,A
    进去就把⻔锁上,其他⼈是不是就进不来了。这样就保证了这段代码的原⼦性了。 有时也把这个现象叫做同步互斥,表⽰操作是互相排斥的。
  • ⼀条 java 语句不⼀定是原⼦的,也不⼀定只是⼀条指令 比如上面的:count++,对应的是多条CPU指令 1:从内存或者寄存器读取count值 LOAD 2:执行自增 ADD 3:把计算结果写回寄存器或者内存 STORE
  • 不保证原⼦性会给多线程带来什么问题 如果⼀个线程正在对⼀个变量操作,中途其他线程插⼊进来了,如果这个操作被打断了,结果就可能是错误的。 这点也和线程的抢占式调度密切相关. 如果线程不是 “抢占” 的, 就算没有原⼦性, 也问题不⼤.
    在这里插入图片描述

在这里插入图片描述

4. 内存可见性

  • 什么是内存可见性 一个线程对共享变量进行了修改,其他线程能感知到变量修改后的值。
  • Java内存模型(JMM) java虚拟机规范定义了Java内存模型 ⽬的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到⼀致的并发效果.
    在这里插入图片描述
  • 分析Java内存模型
    1.工作内存和线程之间是一一对应的
    2.java的共享变量都在主内存里面,java线程线程首先从主内存读取变量的值到自己的工作内存
    3.每个线程都有自己的工作内存,且线程工作内存直接是相互隔离的
    4.线程在工作内存修改完变量的值后,又从工作内存把变量的值刷回主内存里面。
    5.在以上执行count++操作,由于两个线程在执行,每个线程都有自己的工作内存,且相互不可见,最终导致了线程安全问题。线程对共享变量的修改线程之间相互感知不到
  • 注意: 为什么整这么多内存? 实际并没有这么多 “内存”. 这只是 Java 规范中的⼀个术语, 是属于 “抽象” 的叫法,所谓的 “主内存” 才是真正硬件⻆度的 “内存”. ⽽所谓的 “⼯作内存”, 则是指 CPU 的寄存器和⾼速缓存 为啥要这么⿇烦的拷来拷去? 因为
    CPU 访问⾃⾝寄存器的速度以及⾼速缓存的速度, 远远超过访问内存的速度(快了 3 - 4 个数量级,也就是⼏千倍,
    上万倍).那为什么不全部用寄存器,原因很简单,太贵了。
  • 关于JMM内存模型的面试题:JMM规定
    1.所以线程不直接修改主内存中的共享变量
    2.如果修改共享变量,需要把这个变量从主内存复制到自己的工作内存中,修改完之和再刷回主内存
    3.各个线程之间不能相互通信,做到了内存级别的线程隔离。

5. 指令重排序

1.什么是指令重排序 我们写的代码,在编译之后可能与代码对应的指令顺序不同,这个过程就是指令重排序(JVM层面可能重排序,CPU执行指令也可能重排序)
1.一段代码是这样的 a.代阳去教室取英语书 b.代阳去食堂吃饭 c.代阳去教室去数学书 在单线程情况下,JVM,CPU指令集会对其优化,执行顺序按a–c–b的方式执行,也是没有问题,可以少跑一次教室,这就叫指令重排序
编译器对于指令重排序的前提是 “保持逻辑不发⽣变化”. 这⼀点在单线程环境下⽐较容易判断, 但是在多线程环境下就没那么容易了,
多线程的代码执⾏复杂程度更⾼, 编译器很难在编译阶段对代码的执⾏效果进⾏预测, 因此激进的重排序很容易导致优化后的逻辑和之前不等价

总结线程安全问题产生的原因

  1. 线程是抢占式执行的
  • CPU的调度问题,硬件层面,我们解决不了
  1. 多个线程修改同一个变量
  • 在真实业务场景中,使用多线程就是为了提升效率,在并发编成中这个需求是满足的
  1. 原子性
  • 指令是在CPU上执行,怎么才能让CPU在执行时实现原子性,这个可能可以解决
  1. 内存可见性
  • java层面应该可以解决,进程之间可以进行通信,那那么在线程中应该也有这样的机制,让线程在内存中也可以彼此感知
  1. 指令重排序
  • 对于代码来说谁的优先级高,我们可以通过某种方式告诉编译器,不要对我的代码进行重排序
  1. 总结以上1,2我们不能改变,但是3,4,5我们可以进行改变,只要满足3,4,5中的一条或者多条,线程安全问题就可以解决

解决线程安全问题

1. synchronized关键字的介绍(监视器锁 monitor lock)

a.锁的概念

比如线程A拿到了锁,别的线程如果要执行被锁住的代码,那就要等到线程A释放锁之后,如果A没有释放锁,那么别的线程只能阻塞等待,这个状态就是BLOCK

b.synchronized的特性
  1. 互斥:synchronized会引起互斥效果,某个线程执行到某个对象的synchronized时,其他线程如果也执行到同一个对象sychronized就会阻塞等待
  2. 保证了原子性(通过加锁实现)
  3. 保证了内存可见性(通过串行执行实现)
  4. 不保证有序性
c.synchronized的用法

修饰方法:

  1. 修饰非静态方法:默认锁对象是this(当前对象)
  2. 修饰静态方法:默认锁对象是本身类
    修饰代码块:
    可以充当锁对象的是实例对象(new出来的对象,类对象,this)
d.针对上述问题,加synchronied解决问题,分析底层逻辑
    public synchronized void count(){ //  修饰代码块加锁synchronized(this){ //            count+=1; //        }synchronized(this){count+=1;}} }

在这里插入图片描述
如果修饰方法:其实把方法进行了串行化处理
如果修饰的是代码块:其实把修饰代码块的内容,进行了串行话处理。对于部分类似这种要修改共享变量的情况进行串行话,其他代码模块继续并行执行,这样就可以提高效率

画图分析:
在这里插入图片描述
注意的点: t1释放锁之后,也可能第二次还是t1先于t2拿到锁,因为线程是抢占式执行的,不一定是t2
由于线程在执行逻辑之前要拿到锁,当拿到锁时,上一个线程已经执行完所有的指令,并把修改的值刷新会主内存,所有当前线程永远读到的是上一个线程执行完后的值

synchronized保证了原子性
因为当前线程永远拿到的是前一个线程修改后的值,所有这样也现象上实现了内存可见性,但是并没有真正对内存可见性做出技术上的处理。
synchronized没有保证有序性(不会禁止指令重排序)

e.关于synchronized的总结
  1. 被synchronized修身的代码块会编成串行执行
  2. synchronized可以修饰方法或者代码块
  3. 被修饰的代码并不是一次性在CPU执行完,而是中途可能会被CPU调度走,当所有指令执行完后才会释放锁
  4. 只给一个线程加锁,也会出现线程安全
f.不同锁对象的情况
  1. 同·一个引用调用(静态和非静态)两个方法(一个用synchronized修饰一个不用synchronized不修饰)只加一把锁
public static  int count;public  static void main(String[] args) {Counter_Demo1 counter = new Counter_Demo1();Thread t1 =new Thread(()->{for(int i=0;i<5_0000;i++){counter.count();}});Thread t2 =new Thread(()->{for(int i=0;i<5_0000;i++){counter.count1();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: "+counter.count);}public  void count(){//修饰非静态代码块synchronized(this){count+=1;}}public  void count1(){{count+=1;}}
//修饰非静态方法    
//    public void synchronized count(){
//         {
//            count+=1;
//        }
//    }
//    public  void count1(){
//        {
//            count+=1;
//        }
//    }
修饰静态的方法和代码块
//    public static void count(){
//        //修饰代码块
//        //静态方法里面不能用this
//        synchronized(Counter_Demo1.class){
//            count+=1;
//        }
//    }
//    public static void count1(){
//        {
//            count+=1;
//        }
//    }
//    public synchronized static void count(){
//        //修饰代码块
//        //静态方法里面不能用this
//        {
//            count+=1;
//        }
//    }
//    public static void count1(){
//        {
//            count+=1;
//        }
//    }

执行结果
都不符合预期
在这里插入图片描述

  1. 两个引用调用同一个方法(锁对象是实例对象new)
 public static  int count;public  static void main(String[] args) {Counter_Demo1 counter1 = new Counter_Demo1();Counter_Demo1 counter2 = new Counter_Demo1();Thread t1 =new Thread(()->{for(int i=0;i<5_0000;i++){counter1.count();}});Thread t2 =new Thread(()->{for(int i=0;i<5_0000;i++){counter2.count();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: "+counter1.count);}Object object=new Object();public void count(){synchronized (object){count+=1;}}

执行结果不符合预期
在这里插入图片描述
用类对象来加锁

static Object  object= new Object();public void count(){synchronized (object){count+=1;}}

执行结果符合预期
在这里插入图片描述
结论:

  1. 只要一个线程A获得锁,没有锁竞争
  2. 线程A和线程B共同抢一把锁,谁先拿到锁就先执行谁,另一个线程就要阻塞等待,等到持有锁的线程释放锁之后再竞争锁
  3. 线程A与线程B抢的不是同一把锁,它们之间没有竞争关系,分别去拿到自己的锁,不存在锁关系
g.如何判断多个线程竞争的是不是同一把锁
  1. 实例对象:new出来的对象,每个都是单独存在
  2. 类中的属性:类没有用static修饰的变量,每个实例对象都是不同的
  3. 类中的静态成员变量:用static修饰,属于类对象,全局唯一
  4. 类对象:.class文件加载jvm之后的对象,全局唯一
  5. 线程之间是否存在锁竞争,关键是看访问的是不是同一个锁对象,如果是则存在锁竞争,如果不是则不存在锁竞争
h.可重入锁
  • 对同一个锁对象和同一个线程,如果可以重复加锁,称之为不互斥,称之为可重入。
  • 对同一个锁对象和同一线程,如果不可以重复加锁,称之为互斥,就会形成死锁。
  • 已经获取锁对象的线程,如果再多次进行加锁操作,不会产生互斥现象
    在这里插入图片描述
I.锁对象
  • 锁对象记录了获取锁的线程信息
  • 任何对象都可以做锁对象
  • java中每个对象都是由以下几个部分组成:
    – 1.markword
    – 2.类型指针
    – 3.实例数据
    – 4.对齐填充
    – 5对象默认带线啊哦是16byte
    在这里插入图片描述
    在这里插入图片描述

Java 标准库中的线程安全类

不安全类

  • Arraylist
  • LinkedList
  • HashMap
  • TreeMap
  • HashSet
  • TreeSet
  • StringBuilder

安全类

-Vector(不推荐)

  • HashTable(不推荐)

  • CocurrentHashMap

  • StringBuffer

  • String (虽然没有加锁,但是不涉及修饰仍然是线程安全的)

volatile 关键字

解决线程安全的问题

  • 真正意义上解决了内存可见性
  • 解决了指令重排序(禁止指令重排序)问题
  • 没有解决原子性问题

内存可见性

  • 我们都知道,实际工作时候,访问的数据都是工作内存里面的数据,这样是为了保证效率,但是这样有时候会产生安全问题。但是加上 volatile , 强制读写内存. 速度是慢了, 但是数据变的更准确了
  • 代码在写⼊ volatile 修饰的变量的时候
    – 改变线程⼯作内存中volatile变量副本的值
    – 将改变后的副本的值从⼯作内存刷新到主内存
  • 代码在读取volatile修改的变量时候
    –从主内存中读取volatile变量的最新值到线程的⼯作内存中
    –从⼯作内存中读取volatile变量的副本

实例

static class Counter {public volatile int flag = 0;}public static void main(String[] args) {Counter counter = new Counter();Thread t1 = new Thread(() -> {while (counter.flag == 0) {// do nothing}System.out.println("循环结束!");});Thread t2 = new Thread(() -> {Scanner scanner = new Scanner(System.in);System.out.println("输⼊⼀个整数:");counter.flag = scanner.nextInt();});t1.start();t2.start();}
// 执⾏效果
// 当⽤⼾输⼊⾮0值时, t1 线程循环不会结束. (这显然是⼀个 bug)
//static class Counter {
//    public volatile int flag = 0;
//}
// 执⾏效果
// 当⽤⼾输⼊⾮0值时, t1 线程循环能够⽴即结束.

在这里插入图片描述

  • 对于线程t1来说,只是比较flag这个变量的值,从来都没有修改过,所有认为,这个值永远也不会改变,从而也不会重新从主内存中读取值(cpu为了提升高运行效率这个值一般存在寄存器或者cpu的缓存中)

  • 在多线程环境下,就会出现出现这个问题,一个线程修改了另一个线程无法感知到的变量

CPU层面保证可见性

MESI缓存 一致协议(可以理解是一种通知机制)
在这里插入图片描述

Java层面(保证指令顺序,从而保证内存可见性)

内存屏障:作用是保证指令执行的顺序,从而保证内存可见性
在这里插入图片描述
volatile写:
在这里插入图片描述
volatile读:
在这里插入图片描述
有序性:用volatile 修改过的变量,由于前后有内存屏障,保证了指令的执行顺序,也可以理解为告诉编译器,不要进行指令重排序。

总结

volatile不保证原子性

public static volatile int count;public synchronized static void main(String[] args) {Counter counter = new Counter();Thread t1 =new Thread(()->{for(int i=0;i<5_0000;i++){counter.count();}});Thread t2 =new Thread(()->{for(int i=0;i<5_0000;i++){counter.count();}});t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Count: "+counter.count);}public  void count(){count+=1;}

在这里插入图片描述

volatile保证可见性:MESI缓存 一致协议(可以理解是一种通知机制)
volatile保证有序性:内存屏障:作用是保证指令执行的顺序,从而保证内存可见性

wait() 和 notify()

wait和notify的基础知识

  • wait() 和notify(),notifyAll()是object方法
  • wait()/wait(long timeout):让线程进入等待的线程
  • notify()/notifyAll():唤醒在当前对象上等待的线程

wait()方法

  • wait做的事情
    – 使当前执行代码的线程进行等待(把线程放到等待队列中)
    – 释放当前锁
    – 满足一定条件被唤醒,尝试重新获得这个锁
    – wait要搭配sychronized来使用,脱离sychronized使用wait会之间抛出异常
  • wait 结束等待的条件:
    – 其他线程调用 调用该对象的notify方法
    – wait等待时间超时(wait ⽅法提供⼀个带有 timeout 参数的版本, 来指定等待时间)
    – 其他线程调用该等待线程的interrupted方法,导致wait抛出InterruptedException 异常.
  • wait()方法代码使用
public static void main(String[] args) throws InterruptedException {Object object = new Object();System.out.println("等待中");synchronized (object) {object.wait(1000);}System.out.println("等待结束");}这样在执⾏到object.wait()之后就⼀直等待下去,那么程序肯定不能⼀直这么等待下去了。这个时候就
需要使⽤到了另外⼀个⽅法唤醒的⽅法notify()

notify()⽅法

  • notify ⽅法是唤醒等待的线程.
    – 方法notify()也要在同步方法或者同步代码块中执行,该方法是用来通知哪些可能等待该对象的对象锁的其他线程,对其发出通知,并使它们重新获取该对象对象锁
    – 如果由多个线程等待,则有线程调度器随机挑选出一个呈现wait状态的线程。(并没有 “先来后到”))
    – 在notify()方法后,当前线程不会立马释放该对象锁,需要等到notify方法线程将程序执行完,也就是退出同步代码块之后才会释放锁对象。
  • 使用notify()方法唤醒线程
static class WaitTask implements Runnable  {private Object locker;public WaitTask(Object locker) {this.locker = locker;}@Overridepublic void run() {synchronized (locker){try {System.out.println("等待开始") ;locker.wait();System.out.println("等待结束");} catch (InterruptedException e) {e.printStackTrace();}}}}static class NotifyTask implements Runnable {private Object locker;public NotifyTask(Object locker) {this.locker = locker;}@Overridepublic void run() {synchronized (locker) {System.out.println("notify 开始");locker.notify();System.out.println("notify 结束");}}}public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread t1 = new Thread(new WaitTask(locker));Thread t2 = new Thread(new NotifyTask(locker));t1.start();Thread.sleep(1000);t2.start();}

notifyAll()⽅法

  • notify⽅法只是唤醒某⼀个等待线程. 使⽤notifyAll⽅法可以⼀次唤醒所有的等待线程.
 static class WaitTask implements Runnable {private Object locker;public WaitTask(Object locker) {this.locker = locker;}@Overridepublic void run() {synchronized (locker) {try {System.out.println("等待开始");locker.wait();System.out.println("等待结束");} catch (InterruptedException e) {e.printStackTrace();}}}}static class NotifyTask implements Runnable {private Object locker;public NotifyTask(Object locker) {this.locker = locker;}@Overridepublic void run() {synchronized (locker) {System.out.println("notify 开始");locker.notifyAll();System.out.println("notify 结束");}}}public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread t1 = new Thread(new WaitTask(locker));Thread t3 = new Thread(new WaitTask(locker));Thread t4 = new Thread(new WaitTask(locker));Thread t2 = new Thread(new NotifyTask(locker));t1.start();t3.start();t4.start();sleep(1000);t2.start();}}

注意: 虽然是同时唤醒 3 个线程, 但是这 3 个线程需要竞争锁. 所以并不是同时执⾏, ⽽仍然是有先有后的执⾏

wait 和 sleep和join的对⽐(⾯试题)

  1. wait需要搭配synchronized使用 sleep,join不需要
  2. wait是Object的方法,sleep是Thread的静态方法,join是类中的方法(实例方法)
  3. 一个是用于线程之间的通信的,两个是让线程阻塞一段时间
  4. 相同点:可以让线程放弃执行一段时间

wait和notify的总结

在这里插入图片描述

  • join和wait是两个不同的操作
    –join是Thread类中的方法
    – wait和notify是Object类中的方法
    – join状态,主线程要等待子线程的结果
    – wait是等待另一个线程的资源
  • wait和notify必须跟synchronized一起使用,并且使用同一个对象
    – 否则会报错
    在这里插入图片描述
    – wait的线程进入阻塞状态,调用wait的线程会释放自己持有的锁(不再占有cpu资源)
  • notify()和notifyAll(),
    – notify随机唤醒一个线程,notifyAll唤醒所有线程,唤醒后的线程需要重新去竞争锁,拿到锁之后wait位置的代码才会继续执行。
  • 使用小结
    – wait和notify必须搭配synchronized一起使用
    – wait和notify使用的锁对象必须是同一个
    – notify执行多少次都没有关系(及时没有wait)(类似老板把包子做好空喊了一声)
  • 举例:
    – 现实举例:在这里插入图片描述
    – 指令举例
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ElasticSearch如何做性能优化?

大家好&#xff0c;我是锋哥。今天分享关于【ElasticSearch如何做性能优化&#xff1f;】面试题。希望对大家有帮助&#xff1b; ElasticSearch如何做性能优化&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Elasticsearch 中&#xff0c;性能优化是…

Chrome浏览器调用ActiveX控件--allWebOffice控件

背景 allWebOffice控件能够实现在浏览器窗口中在线操作文档的应用&#xff08;阅读、编辑、保存等&#xff09;&#xff0c;支持编辑文档时保留修改痕迹&#xff0c;支持书签位置内容动态填充&#xff0c;支持公文套红&#xff0c;支持文档保护控制等诸多办公功能&#xff0c;…

贪心算法(一)

目录 一、贪心算法 二、柠檬水找零 三、将数组和减半的最少操作次数 四、最大数 五、摆动序列 一、贪心算法 贪心算法的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心策略&#xff1a;1、把解决问题的过程分为若干步。2、解决每一步的时候&#xff…

Scratch节日作品 | 圣诞节礼物——体验节日的温馨与编程的乐趣! ❄️

今天为大家推荐一款充满节日氛围的Scratch作品——《圣诞礼物》&#xff01;这款程序不仅带来了雪花飘落、圣诞老人和麋鹿的经典场景&#xff0c;还通过编程的形式让小朋友们体验到收到礼物的喜悦。通过这款游戏&#xff0c;小朋友们能学习编程知识、了解圣诞文化&#xff0c;同…

基于Qwen2-VL模型针对 ImageToText 任务进行微调训练 - 数据处理

基于Qwen2-VL模型针对 ImageToText 任务进行微调训练 - 数据处理 flyfish 给定的图像生成一段自然语言描述。它的目标是生成一个或多个句子&#xff0c;能够准确地描述图像中的主要内容、物体、动作、场景等信息。例如&#xff0c;对于一张包含一只狗在草地上奔跑的图像&…

Spring Boot整合 RabbitMQ

文章目录 一. 引入依赖二. 添加配置三. Work Queue(工作队列模式)声明队列生产者消费者 四. Publish/Subscribe(发布订阅模式)声明队列和交换机生产者消费者 五. Routing(路由模式)声明队列和交换机生产者消费者 六. Topics(通配符模式)声明队列和交换机生产者消费者 一. 引入依…

谷粒商城—分布式基础

1. 整体介绍 1)安装vagrant 2)安装Centos7 $ vagrant init centos/7 A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on…

【考前预习】1.计算机网络概述

往期推荐 子网掩码、网络地址、广播地址、子网划分及计算-CSDN博客 一文搞懂大数据流式计算引擎Flink【万字详解&#xff0c;史上最全】-CSDN博客 浅学React和JSX-CSDN博客 浅谈云原生--微服务、CICD、Serverless、服务网格_云原生 serverless-CSDN博客 浅谈维度建模、数据分析…

计算机视觉与医学的结合:推动医学领域研究的新机遇

目录 引言医学领域面临的发文难题计算机视觉与医学的结合&#xff1a;发展趋势计算机视觉结合医学的研究方向高区位参考文章结语 引言 计算机视觉&#xff08;Computer Vision, CV&#xff09;技术作为人工智能的重要分支&#xff0c;已经在多个领域取得了显著的应用成果&…

AI智算-k8s部署大语言模型管理工具Ollama

文章目录 简介k8s部署OllamaOpen WebUI访问Open-WebUI 简介 Github&#xff1a;https://github.com/ollama/ollama 官网&#xff1a;https://ollama.com/ API&#xff1a;https://github.com/ollama/ollama/blob/main/docs/api.md Ollama 是一个基于 Go 语言开发的可以本地运…

PyQt事件机制练习

一、思维导图 二、代码 import sysfrom PyQt6.QtTextToSpeech import QTextToSpeech from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QLineEdit from PyQt6 import uic from PyQt6.QtCore import Qt, QTimerEvent, QTimeclass MyWidget(QWidget):d…

硬件设计 | Altium Designer软件PCB规则设置

基于Altium Designer&#xff08;24.9.1&#xff09;版本 嘉立创PCB工艺加工能力范围说明-嘉立创PCB打样专业工厂-线路板打样 规则参考-嘉立创 注意事项 1.每次设置完规则参数都要点击应用保存 2.每次创建PCB&#xff0c;都要设置好参数 3.可以设置默认规则&#xff0c;将…

【计算机学习笔记】GB2312、GBK、Unicode等字符编码的理解

之前编写win32程序时没怎么关注过宽字符到底是个啥东西&#xff0c;最近在编写网络框架又遇到字符相关的问题&#xff0c;所以写一篇文章记录一下&#xff08;有些部分属于个人理解&#xff0c;如果有错误欢迎指出&#xff09; 目录 几个常见的编码方式Unicode和UTF-8、UTF-16、…

深入理解 CSS 文本换行: overflow-wrap 和 word-break

前言 正常情况下&#xff0c;在固定宽度的盒子中的中文会自动换行。但是&#xff0c;当遇到非常长的英文单词或者很长的 URL 时&#xff0c;文本可能就不会自动换行&#xff0c;而会溢出所在容器。幸运的是&#xff0c;CSS 为我们提供了一些和文本换行相关的属性&#xff1b;今…

centos9升级OpenSSH

需求 Centos9系统升级OpenSSH和OpenSSL OpenSSH升级为openssh-9.8p1 OpenSSL默认为OpenSSL-3.2.2&#xff08;根据需求进行升级&#xff09; 将源码包编译为rpm包 查看OpenSSH和OpenSSL版本 ssh -V下载源码包并上传到服务器 openssh最新版本下载地址 wget https://cdn.openb…

Pull requests 和Merge Request其实是一个意思

Pull requests的定义 在Git中&#xff0c;PR&#xff08;Pull Request&#xff09;是一种协作开发的常用方式。它允许开发者将自己的代码变更&#xff08;通常是一个分支&#xff09;提交到项目的仓库中&#xff0c;然后请求负责代码审查的人员将这些变更合并到主分支中。通过…

【ubuntu】将Chroma配置为LINUX服务

Chroma是一个轻量级向量数据库。既然是数据库&#xff0c;那么我希望它是能够长时间运行。最直接的方式是配置为service服务。 可惜官方没有去提供配置为服务的办法&#xff0c;而鄙人对docker又不是特别感冒。所以自己研究了下chroma配置为服务的方式。 系统&#xff1a;ubu…

w~深度学习~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/12663254 #Motion Plan 代码 github.com/liangwq/robot_motion_planing 轨迹约束中的软硬约束 前面的几篇文章已经介绍了&#xff0c;轨迹约束的本质就是在做带约束的轨迹拟合。输入就是waypoint点list&#xff0c;约束…

机器人构建详解:售前售后服务客服机器人与广告生成机器人的微调数据处理方法

引言 大模型&#xff08;如BERT、GPT等&#xff09;在自然语言处理任务中展现了强大的能力&#xff0c;但为了使其更贴合特定应用场景&#xff0c;通常需要进行微调。本文将详细讲解如何为售前售后服务的客服机器人和广告生成机器人准备高质量的微调数据&#xff0c;并通过具体…

cocos中使用SocketIO

Creator版本&#xff1a;v3.8.3 socketIO是socket的一个封装 cocos里集成了websocket但是没有socketIO 下载依赖文件 首先需要下载socketIO代码&#xff0c;版本要和后端保持一致 能npm下载最好npm install socket.io-clientversion(需要指定版本) 但我这一直超时,所以就直接…