当前位置: 首页 > article >正文

【javaEE】多线程(进阶)

1.❤️❤️前言~🥳🎉🎉🎉

Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。

如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的内容感兴趣,记得关注我👀👀以便不错过每一篇精彩。

当然,如果在阅读中发现任何问题或疑问,我非常欢迎你在评论区留言指正🗨️🗨️。让我们共同努力,一起进步!

加油,一起CHIN UP!💪💪

🔗个人主页:E绵绵的博客
📚所属专栏:

1. JAVA知识点专栏

        深入探索JAVA的核心概念与技术细节

2.JAVA题目练习

        实战演练,巩固JAVA编程技能

3.c语言知识点专栏

        揭示c语言的底层逻辑与高级特性

4.c语言题目练习

        挑战自我,提升c语言编程能力

5.Mysql数据库专栏

        了解Mysql知识点,提升数据库管理能力

6.html5知识点专栏

        学习前端知识,更好的运用它

7. css3知识点专栏

        在学习html5的基础上更加熟练运用前端

8.JavaScript专栏

        在学习html5和css3的基础上使我们的前端使用更高级、

9.JavaEE专栏

        学习更高阶的Java知识,让你做出网站

📘 持续更新中,敬请期待❤️❤️

进阶知识我们只需要了解即可,有个认识,因为它工作中基本不用,但面试中可能考 

2.常见的锁策略

这是对于锁的分类。对于该知识我们只需了解即可。

因为我们不自己去实现锁,只是单纯使用锁,所以无需重点理解,只要简单知道。

乐观锁和悲观锁:
乐观锁 :场景中它不太会出现锁冲突
悲观锁 :场景中它非常容易出现锁冲突


重量级锁和轻量级锁:
重量级锁:该锁开销比较大(锁冲突比较多),一个悲观锁,通常是重量级锁(不绝对)
轻量级锁:加锁开销比较小(锁冲突比较少),一个乐观锁通常是轻量级锁(不绝对)


自旋锁和挂起等待锁:
自旋锁是轻量级锁的一种典型实现,在用户态下通过自旋方式(whlie循环)达到加锁。因为是用户态,所以开销较小

挂起等待锁,是一种重量级锁的典型实现,通过内核态借助系统提供的锁机制 当出现锁冲突的时候 会牵扯到内核对于线程的调度 是冲突线程出现挂起(阻塞等待)。因为牵涉到内核,所以开销较大。


读写锁:
把读操作和写操作分别加锁。
如果两个线程 两个线程都是读加锁 不会产生锁竞争
如果两个线程 一个写加锁 另一个线程写加锁 会产生锁竞争
如果两个线程 一个线程写加锁 另一个线程读加锁 会产生锁竞争

(因为两个读并发不会引发线程不安全,读和写并发以及两个写并发会引发线程安全,所以针对该情况就有如上加锁规则)


公平锁 和 非公平锁
公平锁:遵守"先来先到" B比C先来 当A释放锁之后B就能比C先得到锁
非公平锁:不遵守先来后到功能,在A释放锁后b和c重新竞争。

操作系统自带的锁默认都属于非公平锁
如果想实现公平锁,就需要依赖额外的数据结构, 来记录线程们的先后顺序.


可重入锁 和 不可重入锁
如果一个线程对一把锁加锁两次会出现死锁就是不可重入锁
不出现死锁就是可重入锁

3.synchronized原理 

结合上面的锁策略, 我们就可以总结出,Synchronized 具有以下特性(只考虑 JDK 1.8):

1. 开始时是乐观锁, 如果锁冲突频繁, 就转换为悲观锁.

2. 开始是轻量级锁实现, 如果锁被持有的时间较长, 就转换成重量级锁.

3. 实现轻量级锁的时候用到自旋锁策略,实现重量级锁的时候用到挂起等待锁

4. 是一种不公平锁

5. 是一种可重入锁

6. 不是读写锁  

它的原理有三个要讲:锁升级,锁消除,锁粗化。 

锁升级

代码中写了一个synchronized之后 这里可能会产生一系列的"自适应的过程"锁升级:
无锁->偏向锁->轻量级锁->重量级锁
偏向锁: 不是真的锁,并没有加锁,只是做了一个标记 如果有别的锁来竞争了就会真正加锁升级为轻量级锁,如果没有别的锁竞争就不会真的加锁。
加锁本身有一定的开销,能不加就不加,有竞争才加,这是懒汉模式的一个很好体现。
对于加锁成为轻量级锁后,如果竞争这把锁的线程越来越多了(锁冲突更激烈了),就从轻量级锁升级成重量级锁

锁消除

编译器,会智能的判定当前这个代码是否有必要加锁,如果你写了加锁,但是实际上没有必要加锁,编译器就会把加锁操作自动删除掉。它保证优化之后的逻辑和之前的逻辑一致。
比如,在单个线程中,使用StringBuffer,编译器就会进行优化消除掉锁,且不影响逻辑。

编译器的锁消除是非常小心的,如果没有确定十分安全它是不会优化的。

锁粗化

首先要学习锁粗化就要讲下关于锁的粒度
如果加锁操作里包含的实际要执行的代码越多 就认为锁的粒度越大

所以如果有很多粒度较小的锁,就可能短时间内出现频繁创建销毁锁的情况,开销就很大。编译器就会通过合并多个锁操作,减少锁的开销。这就是锁粗化。

总结一下,锁粗化是一种编译器或运行时环境的优化技术,主要用于减少多线程程序中锁操作的开销。它的核心思想是将多个连续的锁操作合并为一个更大的锁操作,从而减少锁的获取和释放次数,提高程序性能。

4.CAS

CAS的概念 

CAS是一种用于实现多线程同步的原子操作。它是现代并发编程中非常重要的底层机制,广泛应用于无锁算法、线程安全数据结构和并发控制中。 

这是CAS伪代码

​
boolean compareAndSwap(V, A, B) {if (&V == A) {&V = B;return true;}return false;
}//这是CAS伪代码,讲述逻辑,不能实现​

CAS 操作包含三个操作数:

  1. 内存位置(V):需要更新的变量的内存地址。

  2. 期望值(A):变量的当前值。

  3. 新值(B):希望将变量更新为的值。

CAS 的操作逻辑是:

  • 如果内存位置 V 的值等于期望值 A,则将 V 的值更新为新值 B,返回true。

  • 如果 V 的值不等于 A,则不做任何操作,返回false。

CAS 的特点 

  1. 原子性
    CAS 操作是硬件级别的原子操作,通常由 CPU 提供支持,然后封装为api在java中使用。

  2. 无锁
    CAS 是一种无锁编程技术,不需要使用传统的锁机制就能解决线程安全问题,因此可以避免锁带来的开销。

CAS的应用

实现原子类 

要实现原子类,标准库中提供了 java.util.concurrent.atomic 包, 里面的类都是基于内部有CAS从而实现了原子性 :这些类的方法使用时都不会被拆分为几个指令,只能当作一个整体(一个指令)来看,所以无需锁就能实现我们要的线程安全效果。

典型的就是 AtomicInteger 类. 其中的 getAndIncrement 相当于 i++ 操作.

public class Demo01_CAS {public static void main(String[] args) throws InterruptedException {//原子整型AtomicInteger atomicInteger = new AtomicInteger();Thread thread = new Thread(() -> {//五万次自增操作for (int i = 0; i < 50000; i++) {atomicInteger.getAndIncrement();}});Thread thread2 = new Thread(() -> {//五万次自增操作for (int i = 0; i < 50000; i++) {atomicInteger.getAndIncrement();}});//启动线程thread.start();thread2.start();//等待两个线程执行完成thread.join();thread2.join();System.out.println(atomicInteger);}
}

最后结果为1w,就可以证明atomicinteger是个原子类,里面的方法都具有原子性,不会被拆分,不用锁就能实现一样的效果,且开销更小。

实现自旋锁 

CAS 可以用于实现自旋锁,即线程在获取锁时不断重试,而不是进入阻塞状态。

public class SpinLock {private AtomicBoolean locked = new AtomicBoolean(false);public void lock() {while (!locked.compareAndSet(false, true)) {// 自旋等待}}public void unlock() {locked.set(false);}
}

对于实现自旋锁了解一下就行,而原子类一般用的比较多,要熟记 

CAS的ABA问题 

ABA 问题是 CAS 的一个经典问题。例如:

  1. 线程 1 读取变量 V 的值为 A

  2. 线程 2 将 V 的值从 A 改为 B,然后又改回 A

  3. 线程 1 执行 CAS 操作,发现 V 的值仍然是 A,误认为V没有发生变化。

通常情况下ABA问题是不会发生bug的,但是特殊情况还是会导致一些问题。

比如我的账户里面有2000块钱(状态A),我委托张三说:如果我忘给李四转1000块钱,下午帮我转一下,我在中午给李四转了1000块钱(状态B),但是随后公司发奖金1000到我的账户,此时我账户有1000块钱(状态A),张三下午检查我账户,发现我有2000块钱,于是又给李四转了1000块钱,此时就出现问题了,李四收到了两次1000元,不符合我们的需求了.

那么解决方案是什么呢?

给预期值加一个版本号.在做CAS操作时,同时要更新预期值的版本号,版本号只增不减,每次操作都加一,在进行CAS比较的时候,不仅预期值要相同,版本号也要相同,这个时候才会返回true.

5.JUC(java.util.concurrent) 的常见类 

concurrent有并发的意思,所以这里的类都是并发编程相关的类 

Callable接口

Callable 接口是 Java 并发编程中的一个重要接口,用于表示一个可以返回结果的任务。它与 Runnable 接口类似,但 Callable 更强大,因为它可以返回结果,而runable返回不了结果。

下面给一个代码示例去教你怎么使用:

代码示例: 创建线程计算 1 + 2 + 3 + ... + 1000, 使用 Callable 版本

1.创建一个匿名内部类, 实现 Callable 接口. Callable 带有泛型参数. 泛型参数表示返回值的类型.

2.重写 Callable 的 call 方法, 完成累加的过程. 直接通过返回值返回计算结果.

3.把 callable 实例使用 FutureTask 包装一下.

4.创建线程, 线程的构造方法传入 FutureTask . 此时新线程就会执行 FutureTask 内部的 Callable 的 call 方法, 完成计算. 计算结果就放到了 FutureTask 对象中.

5.在主线程中调用 futureTask.get() 能够阻塞等待新线程计算完毕. 并获取到 FutureTask 中的线程返回结果.

​
public class Demo {public static void main(String[] args) throws ExecutionException, InterruptedException {Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 1; i < 101; i++) {sum += i;TimeUnit.SECONDS.sleep(1);}return sum;}};//通过FutureTask来创建一个对象,这个对象持有CallableFutureTask<Integer> futureTask = new FutureTask<>(callable);//让线程执行好定义的任务Thread thread = new Thread(futureTask);thread.start();Integer result = futureTask.get();System.out.println("最终的结果为:" + result);}
}​

 ReentrantLock 可重入锁

和synchronized锁类似。这个锁提供了两个方法:lock(上锁) unlock(解锁)

使用这个锁的时候要注意解锁,上锁后,在代码执行过程中,遇到return 或者异常终止了,就可能引起 unlock没有被执行,锁没有释放,其他地方想使用该锁就会死等,因此,正确使用ReentrantLock锁 是把unlock放在finall代码块中,这样无论如何都能防止锁未被释放了。

import java.util.concurrent.locks.ReentrantLock;public class TryLockExample {private final ReentrantLock lock = new ReentrantLock();public void performTask() {if (lock.tryLock()) { // 尝试获取锁try {System.out.println(Thread.currentThread().getName() + " 获取锁,正在执行任务");Thread.sleep(1000); // 模拟耗时操作} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock(); // 释放锁System.out.println(Thread.currentThread().getName() + " 释放锁");}} else {System.out.println(Thread.currentThread().getName() + " 未能获取锁,执行其他逻辑");}}public static void main(String[] args) {TryLockExample example = new TryLockExample();Runnable task = example::performTask;Thread thread1 = new Thread(task, "线程1");Thread thread2 = new Thread(task, "线程2");thread1.start();thread2.start();}
}

对于该锁还有个trylock方法。

  1. boolean tryLock()

    • 尝试获取锁,如果锁可用则立即获取并返回 true;否则返回 false且放弃获取锁并执行后续代码

  2. boolean tryLock(long timeout, TimeUnit unit)

    • 在指定的时间内尝试获取锁。

    • 如果锁在时间段内可获取到,则获取锁并返回 true

    • 如果超时时间到达仍未获取锁,则放弃锁,返回 false

优势:
1.ReentrantLock,在加锁的时候,有两种方式. lock, tryLock. 给了咱们更多的可操作空间。
2.ReentrantLock,提供了公平锁 的实现.(默认情况下是非公平锁)

3. ReentrantLock 提供了更强大的等待通知机制,搭配了 Condition 类实现等待通知(类似于wait,notify)
虽然 ReentrantLock 有上述优势,但是咱们在加锁的时候,还是首选synchronized。因为ReentrantLock 使用更加复杂,尤其是容易忘记解锁,而上述优势不算刚需,另外 synchronized 背后还有一系列的优化手段~~ 

 semaphore 信号量

信号量,⽤来表⽰"可⽤资源的个数".本质上就是⼀个计数器.

就类似停车场:用N记录当前可停车位,

有车进来停车,N-1;有车开走,N+1。这个N就表示可用资源的个数。

设“可⽤资源的个数"用 N来表示:

申请一个资源,会使N-1,称为“P操作”;释放一个资源,会使N+1,称为“V操作”。如果N为0了,继续P操作,该线程则会进行阻塞。

信号量是操作系统内部提供的一种机制,操作系统对应的api被JVM封装下,就能通过java代码来调用其相关的操作了。

在java中,用 acquire方法,表示申请;release方法,表示释放。这两个操作都是具有原子性的,可以直接使用。

对于锁就是一种特殊的信号量,可以认为是计数值为1的信号量:释放锁状态,就是1;加锁状态,就是0。对于这种非0即1的信号量,称为二元信号量。

import java.util.concurrent.Semaphore;public class SemaphoreExample {public static void main(String[] args) {// 创建一个信号量,初始许可数为 4Semaphore semaphore = new Semaphore(4);// 创建一个任务Runnable runnable = new Runnable() {@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " 申请资源");semaphore.acquire(); // 获取许可System.out.println(Thread.currentThread().getName() + " 获取到资源了");Thread.sleep(1000); // 模拟资源占用System.out.println(Thread.currentThread().getName() + " 释放资源了");semaphore.release(); // 释放许可} catch (InterruptedException e) {e.printStackTrace();}}};// 创建多个线程并发执行任务for (int i = 1; i <= 6; i++) {new Thread(runnable, "线程" + i).start();}}
}

由于线程调度的不确定性,每次运行的结果可能略有不同,但整体逻辑是一致的。以下是可能的输出:

注意一个线程可以多次acquire资源 

CountDownLatch 

CountDownLatch 是 Java 并发包 (java.util.concurrent) 中的一个同步工具,用于让一个或多个线程等待其他线程完成操作。它的核心思想是通过一个计数器来实现线程的等待和通知机制。计数器初始化为一个正整数,每当一个线程完成任务时,计数器减 1;当计数器减到 0 时,等待的线程会被唤醒

CountDownLatch 的核心方法:

  1. CountDownLatch(int count)

    • 构造函数,初始化计数器。

  2. void await()

    • 使当前线程等待,直到计数器减到 0。

  3. boolean await(long timeout, TimeUnit unit)

    • 使当前线程等待,直到计数器减到 0 或超时。

  4. void countDown()

    • 将计数器减 1。如果计数器减到 0,则唤醒所有等待的线程。

  5. long getCount()

    • 返回当前计数器的值。

 
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int threadCount = 3;CountDownLatch latch = new CountDownLatch(threadCount); // 初始化计数器Runnable task = () -> {try {System.out.println(Thread.currentThread().getName() + " 开始执行任务");Thread.sleep(1000); // 模拟任务执行System.out.println(Thread.currentThread().getName() + " 完成任务");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 计数器减 1}};// 创建多个线程并发执行任务for (int i = 1; i <= threadCount; i++) {new Thread(task, "线程" + i).start();}System.out.println("主线程等待子线程完成任务");latch.await(); // 主线程等待计数器减到 0System.out.println("所有子线程完成任务,主线程继续执行");}
}

6.线程安全的集合类

Vector,Stack,HashTable,是线程安全的 (但是Vector和hashTable不建议⽤,效率太低,只是方法加了synchronized) , 其他的集合类不是线程安全的.

 在多线程环境中使ArrayList线程安全的方式

1.用synchronized加锁,保证线程安全.


2.使用Collectios.synchronizedList(new ArrayList)


synchronizedList可以让ArrayList对象的关键操作都带上synchronized,这样就不用一个一个去填写synchronized,一键速成


3.使用CopyOnWriteArrayList  

它是JUC包下的一个类,使用的是一种叫写时复制技术来实现的

当要修改一个集合时,先复制这个集合的复本,然后修改复本的数据,修改完成后,用复本覆盖原始集合,而读集合时,则是读取原版,所以对于多线程同时进行读取和修改时,不用加锁也不会引发bug(注意同时进行的操作中 修改最多只能有一个,不然有bug)

优点:在多读少写的情况下,无需加锁就解决了ArrayList的线程安全问题,提高了性能。

缺点:对数组的修改不能太频繁;数组不能太长,这些可能会导致复制操作成本太高。

 

多线程下使用哈希表

 HashMap是线程不安全的,HashTable是线程安全的

 多线程环境使用哈希表可以使用:

1.HashTable
2.ConcurrentHashMap

HashTable是把关键方法上都加了synchronized锁,也就是synchronized给一整个哈希表加了锁。当一个线程对数组中某条链表操作时,任何线程都不能对该数组操作,即使两个线程操作的是不同的链表,不加锁也不会有bug,但还是会进行堵塞,所以HashTable在多线程下的执行效率是很慢的。

ConcurrentHashMap: 对HashTable进行了改进和优化:


1.优化了加锁方式

缩小了锁的粒度,不再将整个数组都加锁,对每个链表都分配了一把锁(将每个链表的头节点对象设为锁),只有当多个线程访问同一个链表时,才会产生锁冲突。这样就降低了锁冲突,提高了效率。


2.充分利用CAS原子操作特性

⽐如size属性通过CAS来更新.避免出现重量级锁的情况.


3.优化了扩容方式

HashTable通过计算负载因子,判断是否需要扩容,达到要扩容的值,就直接扩容:创建新数组,将原来的数据全复制到新数组中。当数据量非常大时,扩容操作会进行的比较慢,表现出来的就是在运行的某一时刻比较慢,不具有稳定性。

ConcurrentHashMap对此进行了优化,通过“化整为零”方式进行扩容,不是一下将全部数据进行拷贝,而是进行分批拷贝

当需要扩容时,先创建一个新的数组,每次将一部分数据拷贝到新数组中,后续每个来操作ConcurrentHashMap的线程,都会参与搬家的过程.每个操作负责搬运⼀⼩部 分元素.这个过程中新老哈希表都存在,扩容结束,删除旧表;这样就很稳定。


4.对读操作不进行加锁

ConcurrentHashMap很特殊,进行读写操作并发执行时并不会引发线程安全问题,所以就无需对读操作进行加锁,能进一步减少开销。

 所以现在我们一般在多线程中都用currenthashmap去使用哈希表。

相关文章:

【javaEE】多线程(进阶)

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…...

python从入门到精通(二十四):python爬虫实现登录功能

这里写目录标题 requests实现登录功能selenium实现登录功能 requests实现登录功能 使用 requests 库结合会话&#xff08;Session&#xff09;来尝试登录。不过豆瓣有反爬虫机制&#xff0c;这种方式可能会受到验证码等因素的限制 import requests import re# 豆瓣登录页面 l…...

Flask Jinja语法总结篇

目录 1️⃣ 变量(Variables) 2️⃣ 条件语句(if 语句) 3️⃣ 循环(for 语句) 4️⃣ 过滤器(Filters) 5️⃣ 宏(Macros,类似于函数) 6️⃣ 模板继承(Template Inheritance) 7️⃣ 包含模板(Include) 8️⃣ Flask 结合 Jinja 总结 Jinja 是 Flask 默认使…...

Vue3实战学习(Element-Plus常用组件的使用(输入框、下拉框、单选框多选框、el-image图片))(上)(5)

目录 一、Vue3工程环境配置、项目基础脚手架搭建、Vue3基础语法、Vue3集成Element-Plus的详细教程。(博客链接如下) 二、Element-Plus常用组件使用。 &#xff08;1&#xff09;el-input。(input输入框) <1>正常状态的el-input。 <2>el-input的disable状态。 <3…...

C++ 链表List使用与实现:拷贝交换与高效迭代器细致讲解

目录 list的使用&#xff1a; 构造与赋值 元素访问 修改操作 容量查询 链表特有操作 拼接&#xff08;Splice&#xff09; C11 新增方法 注意&#xff1a; stl_list的模拟实现&#xff1a; 一、链表节点设计的艺术 1.1 结构体 vs 类的选择 二、迭代器实现的精髓 2…...

安当TDE透明加密技术:为Manus大模型构建用户会话数据保护的“安全金库”

摘要 在人工智能技术深度落地的今天&#xff0c;大模型开发者面临的核心挑战已从算法优化转向数据安全。作为垂直领域大模型的代表&#xff0c;Manus凭借其强大的语义理解与个性化交互能力&#xff0c;在金融、医疗、教育等行业获得广泛应用。然而&#xff0c;其海量的用户会话…...

知乎后台管理系统:数据库系统原理实验1——数据库基础概念

实验背景 通过练习绘制语义网络&#xff0c;加深对于基本概念之间关系的理解和掌握。掌握在VISIO中绘制能准确表达基本概念之间关系的语义网络的技能。了解并比较数据模型的Chen’s表示法和UML表示法。理解关系模型设计中的完整性约束的重要性。掌握在Linux操作系统下远程访问…...

docker compose 以redis为例

常见docker compose 命令 》》注意这个是旧版本的&#xff0c;新版本 docker 与compose 之间没有 - 新版本的 docker compose 把 version 取消了 &#xff0c;redis 默认是没有配置文件的 &#xff0c;nginx&#xff0c;mysql 默认是有的 services:redis:image: redis:lat…...

ES C++客户端安装及使用

1. ES 介绍 Elasticsearch &#xff0c; 简称 ES &#xff0c;它是个开源分布式搜索引擎&#xff0c;它的特点有&#xff1a;分布式&#xff0c;零配 置&#xff0c;自动发现&#xff0c;索引自动分片&#xff0c;索引副本机制&#xff0c; restful 风格接口&#xff0c;多…...

【软件工程】一篇入门UML建模图(状态图、活动图、构件图、部署图)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;软件开发必练内功_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前…...

应急响应--流量分析

&#xff08;一&#xff09;Cobalt Strike流量特征分析 1.HTTP特征 源码特征&#xff1a; 在流量中&#xff0c;通过http协议的url路径&#xff0c;在checksum8解密算法计算后&#xff0c;32位的后门得到的结果是92&#xff0c;64位的后门得到的结果是93&#xff0c;该特征符…...

name ‘bare_metal_version‘ is not mamba_ssm安装

目录 解决方法&#xff1a; 测试ok&#xff1a; mamba_ssm安装报错&#xff0c;windows 安装时&#xff0c; pip install mamba_ssm name bare_metal_version is not defined mamba代码地址&#xff1a; https://github.com/state-spaces/mamba/tree/main 解决方法&…...

自然语言处理:高斯混合模型

介绍 大家好&#xff0c;博主又来给大家分享知识了&#xff0c;今天给大家分享的内容是自然语言处理中的高斯混合模型。 在自然语言处理这个充满挑战与机遇的领域&#xff0c;我们常常面临海量且复杂的文本数据。如何从这些数据中挖掘出有价值的信息&#xff0c;对文本进行有…...

【C++指南】一文总结C++类和对象【中】

&#x1f31f; 各位看官好&#xff0c;我是egoist2023&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f680; 今天来学习C类和对象的语法知识。注意&#xff1a;在本章节中&#xff0c;小编会以Date类举例 &#x1f44d; 如果觉得…...

再聊 Flutter Riverpod ,注解模式下的 Riverpod 有什么特别之处,还有发展方向

三年前我们通过 《Flutter Riverpod 全面深入解析》 深入理解了 riverpod 的内部实现&#xff0c;而时隔三年之后&#xff0c;如今Riverpod 的主流模式已经是注解&#xff0c;那今天就让我们来聊聊 riverpod 的注解有什么特殊之处。 前言 在此之前&#xff0c;我们需要先回忆…...

Go语言集成DeepSeek API和GoFly框架文本编辑器实现流式输出和对话(GoFly快速开发框架)

说明 本文是GoFly快速开发框架集成Go语言调用 DeepSeek API 插件&#xff0c;实现流式输出和对话功能。为了方便实现更多业务功能我们在Go服务端调用AI即DeepSeek接口&#xff0c;处理好业务后再用Gin框架实现流失流式输出到前端&#xff0c;前端使用fetch请求接收到流式的mar…...

docker不停机部署

背景 最近做大疆项目时&#xff0c;后台更新部署时&#xff0c;机场和无人机就会掉线。设备自动重连注册时间比较长&#xff0c;应用长时间不可用。所以需要灰色发布服务。docker-compose的swarm模式可解决此问题。 服务构建脚本Dockerfile # 使用官方Java基础镜像&#xff…...

ZLG嵌入式笔记 | ZLG核心板散热设计指引

在嵌入式系统设计中&#xff0c;散热是影响处理器性能与稳定性的关键问题。本文聚焦于高端嵌入式处理器的散热设计&#xff0c;探讨核心板的热设计与系统级热设计方法&#xff0c;以及导热材料和布局的建议&#xff0c;为解决高温问题提供参考。 用高端嵌入式处理器设计系统&am…...

[Java]使用java进行JDBC编程

首先要从中央仓库下载api(类似驱动程序)&#xff0c;为了链接java和mysql 下载jar包&#xff0c;需要注意的是jar包的版本要和mysql保持一致 下面是新建文件夹lib&#xff0c;把jar包放进去&#xff0c;并添加为库 sql固定的情况下运行 import com.mysql.cj.jdbc.MysqlDataSo…...

MySQL进阶-关联查询优化

采用左外连接 下面开始 EXPLAIN 分析 EXPLAIN SELECT SQL_NO_CACHE * FROM type LEFT JOIN book ON type.card book.card; 结论&#xff1a;type 有All ,代表着全表扫描&#xff0c;效率较差 添加索引优化 ALTER TABLE book ADD INDEX Y ( card); #【被驱动表】&#xff0…...

Ubuntu22.04修改root用户并安装cuda

由于本人工作原因&#xff0c;经常会遇到需要给ubuntu打显卡驱动的问题&#xff0c;虽然说不难吧&#xff0c;但是耐不住机器多&#xff0c;重复多次也就烦了&#xff0c;于是抽出了一点时间&#xff0c;并且在deepseek的帮助之下&#xff0c;写了一个自动安装驱动的脚本&#…...

fiddler+雷电模拟器(安卓9)+https配置

一、fiddler配置 1、开启https代理 2、根证书安装&#xff1a;导出证书系统安装 二、模拟器设置 1、设置网络桥接模式 【点击安装】提示安装成功后保存即可 2、开启root&#xff08;开启adb远程调试&#xff09; 3、开启磁盘写入 4、设置WLAN代理 5、证书安装&#xff1a;物…...

STM32-SPI通信协议

目录 一&#xff1a;什么是通信协议&#xff1f; 二&#xff1a;电路结构 1.硬件电路 2&#xff1a;移位 3&#xff1a;时序图 4.交换字节 三&#xff1a;W25Q64简介 硬件电路&#xff1a;​编辑 存储器地址划分 Dlash操作注意事项 状态寄存器 SPI指令集 四&am…...

【CentOS】搭建Radius服务器

目录 背景简介&#xff1a;Radius是什么&#xff1f;Radius服务器验证原理搭建Radius服务器环境信息yum在线安装配置FreeRADIUS相关文件clients.conf文件users文件重启服务 验证 参考链接 背景 在项目中需要用到Radius服务器作为数据库代理用户的外部验证服务器&#xff0c;做…...

Vue中自定义指令:ClickOutside(点击当前模块外的位置)

应用场景 假设我们有一个下拉框组件&#xff0c;当下拉框展开的时候&#xff0c;点击下拉框之外的元素可以自动关闭下拉框。 一 ClickOutside代码示例 在vue3中使用ClickOutside // 导入自定义指令 import { ClickOutside as vClickOutside } from element-plus;// 绑定指令…...

如何在rust中解析 windows 的 lnk文件(快捷方式)

一、从标题二开始看&#x1f601; 这些天在使用rust写一个pc端应用程序&#xff0c;需要解析lnk文件获取lnk的图标以及原程序地址&#xff0c;之前并没有过pc端应用程序开发的经验&#xff0c; 所以在广大的互联网上游荡了两天。额&#x1f97a; 今天找到了这个库 lnk_parse很…...

2019年蓝桥杯第十届CC++大学B组真题及代码

目录 1A&#xff1a;组队&#xff08;填空5分_手算&#xff09; 2B&#xff1a;年号字符&#xff08;填空5分_进制&#xff09; 3C&#xff1a;数列求值&#xff08;填空10分_枚举&#xff09; 4D&#xff1a;数的分解&#xff08;填空10分&#xff09; 5E&#xff1a;迷宫…...

奇安信 2025 年护网蓝队初选笔试题(附答案解析)

&#x1f525; 爆款 CSDN 题库 | 超全护网蓝队笔试真题 | 含详细答案解析 &#x1f525; 熬夜为大家整理了 奇安信 2025 年护网蓝队初选笔试题&#xff0c;&#xff08;关注我我会持续更新&#xff09;涵盖 SQL 注入、Web 安全、渗透测试、二进制安全 等核心知识点&#xff0c;…...

jdk-21_linux-x64_bin.tar.gz Linux jdk21压缩包安装保姆级(详细安装教程)

jdk-21_linux-x64_bin.tar.gz 解压版详细安装教程 一、简洁版&#xff08;需要对 Linux 操作有一定基础&#xff09;二、图文详细教程1、前置准备2、解压安装3、配置环境变量4、验证成功 官网下载地址&#xff1a; https://www.oracle.com/java/technologies/downloads/#java2…...

第6章 定时器计数器

目录 6.1 定时计数器的结构框图 6.2 定时器的控制字 6.2.1 TMOD&#xff1a;工作方式控制寄存器 6.2.2 定时/计数器控制寄存器TCON 6.3 定时/计数器的4种工作方式 6.3.1 方式0、方式1&#xff08;13位、16位定时计数方式&#xff09; 6.3.2 方式2(常数自动重装入) 6.3.3 方…...