JUC并发编程(JUC核心类、TimeUnit类、原子操作类、CASAQS)附带相关面试题
目录
1.JUC并发编程的核心类
2.TimeUnit(时间单元)
3.原子操作类
4.CAS 、AQS机制
1.JUC并发编程的核心类
虽然java中的多线程有效的提升了程序的效率,但是也引发了一系列可能发生的问题,比如死锁,公平性、资源管理以及如何面对线程安全性带来的诸多危害。为此,java就提供了一个专门的并发编程包java.util.concurrent(简称JUC)。此包能够有效的减少了竞争条件和死锁问题。
以下介绍JUC包中核心的类
类名 | 描述 |
---|---|
Executor | Executor 是一个接口,定义了一种执行任务的方式,其目的是将任务的提交与任务的执行解耦。 |
ExecutorService | ExecutorService 是 Executor 的子接口,提供了更丰富的功能,例如线程池管理和任务提交等。 |
ScheduledExecutorService | ScheduledExecutorService 是 ExecutorService 的子接口,可以按照计划(时间或延迟)来执行任务。 |
CompletionService | CompletionService 是一个用于异步执行任务并获取已完成任务结果的框架。 |
Callable | Callable 是一个代表可以返回结果或抛出异常的任务的接口。它类似于 Runnable 接口,但具有返回值。 |
Future | Future 是一个可用于获取异步计算结果的接口。 |
ReentrantLock | ReentrantLock 是一个可重入锁,它提供了更灵活的同步控制和更高级别的功能。 |
BlockingQueue | BlockingQueue 是一个支持阻塞操作的队列,提供了线程安全的生产者-消费者模式的实现。 |
CountDownLatch | CountDownLatch 是一个同步辅助类,允许一个或多个线程等待其他线程完成操作后再继续执行。 |
CyclicBarrier | CyclicBarrier 是一个同步辅助类,使得一组线程能够互相等待,直到所有线程都达到某个公共屏障点。 |
2.TimeUnit(时间单元)
这个类能够非常好的让我们实现各种时间之间的转换。TimeUnit类的是枚举类,里面有DAYS(天),HOURS(小时),MINUTES(分钟),SECONDS(秒),MILLISECONDS(毫秒),NANNOSECONDS(纳秒)
TimeUnit类中常用的方法:
方法签名 | 描述 |
---|---|
public long convert(long sourceDuration, long srcDuration) | 该方法用于将给定的时间源持续时间转换为目标持续时间。 |
public void sleep(long timeout) throws InterruptedException | 该方法使当前线程进入休眠状态,暂停执行一段指定的时间(以毫秒为单位)。如果在休眠期间中断了线程,则会抛出 InterruptedException 异常。 |
具体应用案例:
1.时间转换与输出一个月后的日期
package Example2101;import java.util.Date;
import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args) {
// 五个小时时间long hours = 5;
// 通过SECONDS类将5个小时转为秒long seconds = TimeUnit.SECONDS.convert(hours,TimeUnit.HOURS);System.out.println(seconds);// 获取当前时间long now = System.currentTimeMillis();long furture = now + TimeUnit.MILLISECONDS.convert(30,TimeUnit.DAYS);System.out.println("Now Time is"+new Date(now));Date futureDay = new Date(furture);System.out.println("after mounth time is"+futureDay);}
}
案例2:定义一个闹钟,这个闹钟在5天后会自动发送消息
这种闹钟形式可以通过线程的睡眠机制进行完成,但是一般情况下如果使用线程的睡眠Thread.sleep()里面放的是毫秒,如果要睡眠五天,那么需要设置的数值会非常非常大的,所以可以使用TimeUnit类的睡眠方法实现自定义睡眠。
package Example2102;import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args) {new Thread(()->{try {
// 通过TimeUnit下的Days类的sleep函数定义五天时间TimeUnit.DAYS.sleep(5);System.out.println("闹钟响了!!!!!!");}catch (InterruptedException e){e.printStackTrace();}},"闹钟").start();}
}
3.原子操作类
问题引出:一般情况下如果多线程进行竞争一个变量时候会引发数据错乱的问题。比如多线程下售票员售票案例,由于多个线程竞争,一张票可能已经被卖出去了,但是其他的售票员并不知道,继续售卖同一张票。在之前的时候我们通过了Sychronized()同步位解决了这个问题。但是用这个方法也有不小的弊端,那就是程序效率会大大下降。为此JUC提供了一个新的方式解决这个问题,那就是原子操作类。
首先理解原子性,原子是不可分割的最小物体,在编程中是指一种操作要么做了,要么不做。不可以中断的一种操作。原子操作类具有更高效率,更安全,更简单用法
原子操作类分为很多类,大致分为4类:
基本类型:AtomicInteger 、AtomicLong、AtomicBoolean
数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
引用类型:AtomicReference、AtomicStampedReference、AtomicMarkableReference;
对象属性修改类型:
AtomicIntegerFieldUpdater;AtomicLongFiledUpdater;AtomicReferenceFieldUpdater;
1.基本类型的原子操作类
基本类型:AtomicInteger 、AtomicLong、AtomicBoolean
基本类型之间的操作是差不多的,这里用AtomicLong举例
AtomicLong的常用方法
方法 | 描述 |
---|---|
AtomicLong(long initValue) | 创建一个新的AtomicLong实例,并设置初始值为initValue。 |
get() | 获取当前存储在AtomicLong中的值。 |
set(long newValue) | 将AtomicLong的值设置为newValue。 |
getAndIncrement() | 先获取当前存储在AtomicLong中的值,然后将AtomicLong的值增加1。返回先前的值。 |
setAndIncrement() | 将AtomicLong的值增加1。返回增加前的值。 |
decrementAndGet() | 将AtomicLong的值减少1,并返回减少后的值。 |
使用类方法的关键就在于熟悉add(增加) decrement(自减)increment(自增) set(设置值) get(获取类内部的数据) 方法就是这几个操作之间的组合
案例代码:多个售票员售卖100张票
package Example2103;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;public class javaDemo {public static void main(String[] args) {
// 创建原子操作类AtomicInteger ticket = new AtomicInteger(6);AtomicInteger flag = new AtomicInteger(1);//标志还有票// 创建三个线程进行售票for (int i =0;i<3;i++){new Thread(()->{while (ticket.get()>0){System.out.println("售票员"+Thread.currentThread().getName()+"售卖第"+ticket.decrementAndGet()+"张票");try {
// 设置两秒睡眠TimeUnit.SECONDS.sleep(2);}catch (Exception e){e.printStackTrace();}
// 如果没有票了就将标志位的值设置为0,表示没有票了if (ticket.get() == 0){flag.set(0);System.out.println("卖完了");}}}).start();}}
}
可以看到即使没有使用同步机制也实现了同步的效果。
2.数组原子操作类
数组原子操作类有:AtomicArrayInteger AtomicLongArray AtomicReferenceArray(对象数组)
由于三者这件的使用区别不大,所以这里展示AtomicReferenceArray
AtomicReferenceArray常用方法:
方法 | 描述 |
---|---|
AtomicReferenceArray(int length) | 构造一个指定长度的AtomicReferenceArray对象。 |
AtomicReferenceArray(E[] array) | 使用给定数组初始化AtomicReferenceArray对象。 |
int length() | 返回AtomicReferenceArray的长度(即元素个数)。 |
boolean compareAndSet(int index, E expect, E update) | 将指定索引位置的元素与期望值进行比较,如果相等,则将其更新为新的值。该操作是原子性的,返回是否更新成功。 |
E get(int index) | 获取指定索引位置的元素的值。 |
void set(int index, E newValue) | 设置指定索引位置的元素的值为newValue。 |
E getAndSet(int index, E newValue) | 获取指定索引位置的元素的当前值,并将其设置为newValue。 |
案例代码:
package Example2104;import java.util.concurrent.atomic.AtomicReferenceArray;public class javaDemo {public static void main(String[] args) {String data[] = new String[]{"王二狗","180","130"};
// 初始化AtomicReferenceArray<String> array = new AtomicReferenceArray<String>(data);
// 对象数组的操作System.out.println("身高是:"+array.get(1));array.set(2,"150");System.out.println(array.get(0)+"在拼命锻炼后体重变成:"+array.get(2));
// 筛选如果名字是王二狗的自动改名王二array.compareAndSet(0,"王二狗","王二");System.out.println("改名后名字叫"+array.get(0));}
}
3.引用原子操作类
引用类型:AtomicReference、AtomicStampedReference、AtomicMarkableReference;
其中AtomicReference是可以直接引用数据类型的原子性操作
下面是AtomicReference的常用方法:
方法 | 描述 |
---|---|
AtomicReference() | 无参构造方法,创建一个初始值为null的AtomicReference对象。 |
V get() | 获取当前AtomicReference对象持有的值。 |
void set(V newValue) | 设置AtomicReference对象的值为newValue。 |
boolean compareAndSet(V expect, V update) | 将AtomicReference对象的值与期望值expect进行比较(==比较),如果相等,则将其更新为新值update。该操作是原子性的,返回是否更新成功。 |
V getAndSet(V newValue) | 先获取当前AtomicReference对象的值,然后将其设置为newValue,并返回原来的值。 |
案例代码:使用AtomicReference进行引用操作
package Example2106;import java.util.concurrent.atomic.AtomicReference;
// 创建普通人类
class Person{private int age;private String name;private int id;Person(int age,String name,int id){this.age = age;this.name = name;this.id = id;}
}public class javaDemo {public static void main(String[] args) {Person person1 = new Person(18,"张三",001);Person person2 = new Person(20,"王思",1002);
// 传入person1对象AtomicReference<Person> person = new AtomicReference<Person>(person1);
// 输出对象地址System.out.println(person.get());
// 更改引用对象person.set(person2);System.out.println(person.get());}
}
AtomicStampedReference 基于版本号的数据引用。其中版本号是自己定义的int数据类型
下面是AtomicStampedReference的常用方法:
方法 | 描述 |
---|---|
AtomicStampedReference(V initRef, int initStamp) | 构造一个AtomicStampedReference对象,初始引用值为initRef,初始标记值(戳)为initStamp。 |
V getReference() | 获取当前AtomicStampedReference对象持有的引用值。 |
void set(V newRef, int newStamp) | 设置AtomicStampedReference对象的引用值为newRef,标记值(戳)为newStamp。 |
boolean compareAndSet(V expectRef, V newRef, int expectStamp, int newStamp) | 将AtomicStampedReference对象的引用值与期望值expectRef、标记值(戳)与期望值expectStamp进行比较,如果相等,则将其更新为新值newRef和newStamp。该操作是原子性的,返回是否更新成功。 |
int attemptStamp(V expectedReference, int newStamp) | 如果当前引用值等于expectedReference,则尝试将标记值(戳)更新为newStamp。如果更新成功,返回新的标记值(戳),否则返回当前标记值。 |
int getStamp() | 获取当前AtomicStampedReference对象持有的标记值(戳)。 |
案例代码:
package Example2107;import java.util.concurrent.atomic.AtomicStampedReference;class Draw{private String content = "";private String autor = "";private String title ="";Draw(String content,String autor,String title){this.content =content;this.autor = autor;this.title = title;}public void setContent(String content) {this.content = content;}public String getContent() {return content;}
}
public class javaDemo {public static void main(String[] args) {Draw draw1= new Draw("","alphaMilk","JUC并发编程原子操作类");
// 初始化内容,版本号为1AtomicStampedReference<Draw> atomicDraw = new AtomicStampedReference<Draw>(draw1,1);System.out.println(atomicDraw.getReference());
// 更新内容,版本号更改draw1.setContent("Hello,word");atomicDraw.set(draw1,2);
// 获取当前版本System.out.println(atomicDraw.getStamp());}
}
AtomicMarkableReference与AtomicStampedReference的区别在于,一个是设置boolean类型的初始化标记,一个多设置的是int类型版本号
下面是AtomicMarkableReference的常用方法:
方法 | 描述 |
---|---|
AtomicMarkableReference(V initRef, boolean initMark) | 构造一个AtomicMarkableReference对象,初始引用值为initRef,初始标记值为initMark。 |
V getReference() | 获取当前AtomicMarkableReference对象持有的引用值。 |
boolean isMarked() | 判断当前AtomicMarkableReference对象是否被标记。 |
boolean compareAndSet(V expectRef, V newRef, boolean expectMark, boolean newMark) | 将AtomicMarkableReference对象的引用值与期望值expectRef、标记值与期望值expectMark进行比较,如果相等,则将其更新为新值newRef和newMark。该操作是原子性的,返回是否更新成功。 |
void set(V newRef, boolean newMark) | 设置AtomicMarkableReference对象的引用值为newRef,标记值为newMark。 |
boolean attemptMark(V expectedReference, boolean newMark) | 如果当前引用值等于expectedReference,则尝试将标记值更新为newMark。如果更新成功,返回true,否则返回false。 |
案例代码:
一个班统计同学是否交了班费
package Example2108;import java.util.concurrent.atomic.AtomicMarkableReference;class Student{private String name;private int id;Student(String name,int id){this.name = name;this.id = id;}
}
public class javaDemo {public static void main(String[] args) {Student stu1 = new Student("王一",001);Student stu2 = new Student("张二蛋",002);
// 王一交过班费AtomicMarkableReference<Student> atoStu = new AtomicMarkableReference<Student>(stu1,true);System.out.println(atoStu.getReference());if (atoStu.isMarked()){System.out.println("该同学交过班费");}else System.out.println("该同学尚未交过班费");
// 张二蛋没有交班费atoStu.set(stu2,false);System.out.println(atoStu.getReference());if (atoStu.isMarked()){System.out.println("该同学交过班费");}else System.out.println("该同学尚未交过班费");}
}
4.对象属性修改原子类
AtomicIntegerFieldUpdater;AtomicLongFiledUpdater;AtomicReferenceFieldUpdater;
这三个类的实现原理基本差不多,所以将用AtomicIntegerFieldUpdater举例:
以下是AtomicIntegerFieldUpdater类的常用方法:
int addAndGet(T obj, int data) | 将指定对象obj的字段值与data相加,并返回相加后的结果。 |
boolean compareAndSet(T obj, int expect, int update) | 将指定对象obj的字段值与期望值expect进行比较,如果相等,则将其更新为新值update。返回是否更新成功。 |
int get(T obj) | 获取指定对象obj的字段值。 |
int getAndSet(T obj, int newValue) | 获取指定对象obj的字段值,并将其设置为新值newValue。 |
int decrementAndGet(T obj) | 将指定对象obj的字段值减1,并返回减1后的结果。 |
int incrementAndGet(T obj) | 将指定对象obj的字段值加1,并返回加1后的结果。 |
案例代码:
package Example2109;import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;class Book{volatile long id;volatile String name;Book(long id, String name){this.id = id;this.name = name;}}public class javaDemo {public static void main(String[] args) {Book book1 = new Book(114514,"Java从入门到入土");AtomicReferenceFieldUpdater<Book,String> bookmanger = AtomicReferenceFieldUpdater.newUpdater(Book.class,String.class,"name");System.out.println("更新前书本名称为:"+bookmanger.get(book1));bookmanger.set(book1,"Java从入门到项目实战");System.out.println("更新后书本名称为:"+bookmanger.get(book1));}
}
4.CAS 、AQS机制
CAS是一条CPU并发原语。它的功能是判断某个内存某个位置的值是否相等,如果是则改为新的值,这个操作过程属于原子性操作。
CAS是乐观锁,是一种冲突重试机制,在并发竞争不是很剧烈的情况下,其操作性能会好于悲观锁机制(Synchronization同步处理)
*面试题为什么说 Synchronized 是一个悲观锁?乐观锁的实现原理又是什么?什么是 CAS,它有什么特性?
- Synchronized的并发策略是悲观的,不管是否产生竞争,任何数据的操作都必须加锁。
- 乐观锁的核心是CAS,CAS包括内存值、预期值、新值,只有当内存值等于预期值时,才会将内存值修改为新值。
*面试题:乐观锁一定就是好的吗?
- 乐观锁认为对一个对象的操作不会引发冲突,所以每次操作都不进行加锁,只是在最后提交更改时验证是否发生冲突,如果冲突则再试一遍,直至成功为止,这个尝试的过程称为自旋。
- 乐观锁没有加锁,但乐观锁引入了ABA问题,此时一般采用版本号进行控制;
- 也可能产生自旋次数过多问题,此时并不能提高效率,反而不如直接加锁的效率高;
- 只能保证一个对象的原子性,可以封装成对象,再进行CAS操作;
*面试题:volatile 关键字的作用
对于可见性,Java 提供了 volatile 关键字来保证可见性和禁止指令重排。 volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
从实践角度而言,volatile 的一个重要作用就是和 CAS 结合,保证了原子性,详细的可以参见 java.util.concurrent.atomic 包下的类,比如 AtomicInteger。
volatile 常用于多线程环境下的单次操作(单次读或者单次写)。
相关文章:

JUC并发编程(JUC核心类、TimeUnit类、原子操作类、CASAQS)附带相关面试题
目录 1.JUC并发编程的核心类 2.TimeUnit(时间单元) 3.原子操作类 4.CAS 、AQS机制 1.JUC并发编程的核心类 虽然java中的多线程有效的提升了程序的效率,但是也引发了一系列可能发生的问题,比如死锁,公平性、资源管理…...

个人用C#编写的壁纸管理器 - 开源研究系列文章
今天介绍一下笔者自己用C#开发的一个小工具软件:壁纸管理器。 开发这个小工具的初衷是因为Windows操作系统提供的功能个人不满意,而且现在闲着,所以就随意写了个代码。如果对读者有借鉴参考作用就更好了,能够直接代码段复用即可。…...
iTextSharp 生成PDF
示例代码定义了一个名为PdfController的API控制器,其中的GeneratePdf方法创建了一个新的PDF文档,并将内容添加到文档中。最后,将文档内容转换为字节数组,并通过File方法返回给前端。 注意,你需要在你的项目中添加对iT…...

基于微信小程序的传染病酒店隔离平台设计与实现(Java+spring boot+MySQL+微信小程序)
获取源码或者论文请私信博主 演示视频: 基于微信小程序的传染病酒店隔离平台设计与实现(Javaspring bootMySQL微信小程序) 使用技术: 前端:html css javascript jQuery ajax thymeleaf 微信小程序 后端:…...

vue3中用watch监听响应式数据的注意点
如果你在vue3中使用reactive()方法创建响应式数据,然后又用torefs()方法将响应式数据解构成单一的ref响应式数据。 此时,如果你想用watch监听解构出来单一的响应式数据,watch不起作用。 此时,你需要用watch监听之前的reactive()…...

Jmeter(五) - 从入门到精通 - 创建网络计划实战和创建高级Web测试计划(详解教程)
1.简介 上一篇中已经将其的理论知识介绍了一下,这一篇就带着大家一步一步的把上一篇介绍的理论知识实践一下,然后再说一下如何创建高级web测试计划。 2.网络计划实战 通过上一篇的学习,将其分类为: (1)不需…...

【单片机】51单片机,TLC2543,驱动程序,读取adc
TLC2543 是一款 12 位精密模数转换器 (ADC)。 1~9、11、12——AIN0~AIN10为模拟输入端; 15——CS 为片选端; 17——DIN 为串行数据输入端;(控制字输入端,用于选择转换及输出数据格式) 16——…...
誉天HCIE-Cloud_Computing3.0课程简介
课时:60 第一天 1. 华为云 Stack 解决方案及架构介绍 3. 华为云 Stack 的安装流程解析及规划设计 4. 华为云 Stack 的网络平面的规划解析 5. 华为云 Stack Deploy 部署工具的安装,配置,创建工程,下载 LLD 表 6. 华为云 Stack 的 …...

Unity之ShaderGraph 节点介绍 Procedural节点
程序化 噪声Gradient Noise(渐变或柏林噪声)Simple Noise(简单噪声)Voronoi(Voronoi 噪声) 形状Ellipse(椭圆形)Polygon(正多边形)Rectangle(矩形…...

期权定价模型系列【1】—BSM通用式模型
这是期权定价模型专栏的第一篇文章,此专栏旨在分享一些期权定价模型,将会从最基础的BSM模型开始写起,逐步扩散到蒙特卡洛模拟、二叉树等数值法模型,以及跳跃扩散模型、随机波动率模型,神经网络模型等等。 如果你觉得有…...

HA3 SQL样本实验:一种混合计算查询的全新样本解决方案
作者:陆唯一(芜霜) HA3(对外开源代号:Havenask )是阿里智能引擎团队自研的大规模分布式检索系统,广泛应用于阿里内部的搜索业务,是十多年来阿里在电商领域积累下来的核心竞争力产品。Ha3 SQL 是在原有Ha3引…...
Linux(Web与html)
域名 DNS与域名: 网络是基于tcp/ip协议进行通信和连接的 tcp/ip协议是五层协议:应用层–传输层—网络层----数据链路层----物理层每一台主机都有一个唯一的地址标识(固定的ip地址,用于区分用户和计算机。 ip地址:由…...

SpringBoot 底层机制分析[上]
文章目录 分析SpringBoot 底层机制【Tomcat 启动分析Spring 容器初始化Tomcat 如何关联Spring 容器】[上]搭建SpringBoot 底层机制开发环境Configuration Bean 会发生什么,并分析机制提出问题:SpringBoot 是怎么启动Tomcat ,并可以支持访问C…...

电源控制--对数与db分贝
在控制理论中,"db"通常表示分贝(decibel)的缩写。分贝是一种用于度量信号强度、增益或衰减的单位。 在控制系统中,分贝常用于描述信号的增益或衰减。通常,增益以正数的分贝值表示,而衰减以负数的…...
LeetCode 1749. 任意子数组和的绝对值的最大值(前缀和)
题目: 链接:LeetCode 1749. 任意子数组和的绝对值的最大值 难度:中等 给你一个整数数组 nums 。一个子数组 [numsl, numsl1, …, numsr-1, numsr] 的 和的绝对值 为 abs(numsl numsl1 … numsr-1 numsr) 。 请你找出 nums 中 和的绝对…...

python爬虫相关
目录 初识爬虫 爬虫分类 网络爬虫原理 爬虫基本工作流程 搜索引擎获取新网站的url robots.txt HTHP协议 Resquests模块 前言: 安装 普通请求 会话请求 response的常用方法 简单案例 aiohttp模块 使用前安装模块 具体案例 数据解析 re解析 bs4…...

PAT(Advanced Level) Practice(with python)——1023 Have Fun with Numbers
Code N int(input()) D_N 2*N # print(Yes)if len(str(D_N))>len(str(N)):print(No) else:for s in str(D_N):if s not in str(N) or str(D_N).count(s)!str(N).count(s):print("No")breakelse:print(Yes) print(D_N)...

springboot vue 初步集成onlyoffice
文章目录 前言一、vue ts1. 安装依赖2. onlyoffice组件实现(待优化)3. 使用组件4. 我的配置文件 二、springboot 回调代码1. 本地存储 三、效果展示踩坑总结问题1问题2 前言 对接onlyoffice,实现文档的预览和在线编辑功能。 一、vue ts …...

Win10语言设置 - 显示语言和应用语言
前言 Win10的语言设置可以设置显示语言和应用语言。其中,显示语言用于显示系统文字;应用语言用于应用程序显示文字。下文介绍如何设置。 显示语言 打开系统设置,选择时间和语言,如下图: 修改Windows显示语言即可更…...
RxJava的前世【RxJava系列之设计模式】
一. 前言 学习RxJava,少不了介绍它的设计模式。但我看大部分文章,都是先将其用法介绍一通,然后再结合其用法,讲解其设计模式。这样当然有很多好处,但我个人觉得,这种介绍方式,对于没有接触过Rx…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...