【JavaEE初阶】多线程重点知识以及常考的面试题-多线程进阶(二)
本篇博客给大家带来的是多线程中synchronize的实现原理和JUC(java.util.concurrent) 常见类的相关知识点.
🐎文章专栏: JavaEE初阶
🚀若有问题 评论区见
❤ 欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .
王子,公主请阅🚀
- 要开心
- 要快乐
- 顺便进步
- 1. synchronized原理
- 1.1 加锁工作过程
- 1.1.1 偏向锁
- 1.1.2 轻量级锁
- 1.1.3 重量级锁
- 1.2 锁消除
- 1.3 锁粗化
- 1.4 相关面试题
- 2.JUC(java.util.concurrent) 的常见类
- 2.1 Callable接口
- 2.2 ReentrantLock
- 2.3 原子类
- 2.4 线程池
- 2.5 信号量Semaphore
- 2.6 CountDownLach
- 2.7 相关面试题
要开心
要快乐
顺便进步
1. synchronized原理
synchronized的基本特点(只考虑JDK1.8):
1. 一开始还是乐观锁,如果锁冲突频繁,就转换为悲观锁.
2. 开始是轻量级锁,如果锁被持有时间较长,就转换成重量级锁.
3. 实现轻量级锁时大概率用自旋锁策略.
4. 是一种不公平锁.
5. 是一种可重入锁.
6. 不是读写锁.
1.1 加锁工作过程
JVM将synchronized锁分为无锁,偏向锁,轻量级锁,重量级锁. 会根据实际情况, 进行一次升级.
1.1.1 偏向锁
第一个尝试加锁的线程, 优先进入偏向锁状态.
偏向锁不是真的 “加锁”, 只是给对象头中做一个 “偏向锁的标记”, 记录这个锁属于哪个线程.
如果后续没有其他线程来竞争该锁, 那么就不用进行其他同步操作了(避免了加锁解锁的开销)
如果后续有其他线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了, 很容易识别当前申请锁的线程是不是之前记录的线程), 那就取消原来的偏向锁状态, 进入一般的轻量级锁状态.
偏向锁本质上相当于 “延迟加锁” . 能不加锁就不加锁, 尽量来避免不必要的加锁开销.
举个例子来理解偏向锁:
假设我是一个长的好看又有才华的小姐姐, 某一次吃饭,我遇见了一个非常符合我审美的小哥哥,巧的是他还主动来加我微信,后面我就约他出来玩,一举将他拿下.
成为情侣一段时间后,我发现我腻歪了,想分手了, 此时我就得给他找事,比如: 看到他跟女生聊天就问"你是不是不爱我了",有点小事就跟他吵架,把小事上升到分手.
上述纯属段子. 不难发现,成为情侣后,若是想分手,成本非常的高.
重点来了, 偏向锁是我约小哥哥出来玩,虽然走得很近,但是不表白,这样一来想脱身就非常容易. 这就是偏向锁的加锁过程. 先标记,不上锁.
1.1.2 轻量级锁
随着其他线程进入竞争, 偏向锁状态被消除, 进入轻量级锁状态(自适应的自旋锁).此处的轻量级锁就是通过 CAS 来实现
通过 CAS 检查并更新一块内存 (比如 null => 该线程引用)
如果更新成功, 则认为加锁成功
如果更新失败, 则认为锁被占用, 继续自旋式的等待(并不放弃 CPU).
自旋操作是一直让 CPU 空转, 比较浪费 CPU 资源.
因此此处的自旋不会一直持续进行, 而是达到一定的时间/重试次数, 就不再自旋了.也就是所谓的 “自适应”
1.1.3 重量级锁
如果竞争进一步激烈, 自旋不能快速获取到锁状态, 就会膨胀为重量级锁此处的重量级锁就是指用到内核提供的 mutex .
执行加锁操作, 先进入内核态.
① 在内核态判定当前锁是否已经被占用
② 如果该锁没有占用, 则加锁成功, 并切换回用户态.
③ 如果该锁被占用, 则加锁失败. 此时线程进入锁的等待队列, 挂起等待被操作系统唤醒.
④ 经历了一系列的沧海桑田, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒这个线程, 尝试重新获取锁.
1.2 锁消除
编译器+JVM 判断锁是否可消除. 如果可以, 就直接消除.
有些应用程序的代码中, 用到了 synchronized, 但其实没有在多线程环境下. (例如 StringBuffer)
StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");
sb.append("d");
此时每个 append 的调用都会涉及加锁和解锁. 但如果只是在单线程中执行这个代码, 那么这些加锁解
锁操作是没有必要的, 白浪费了一些资源开销.
锁消除是编译器在编译过程出发的,还没到运行时.
偏向锁是运行时的事情,根据多线程调度情况的不同来变化.
1.3 锁粗化
一段逻辑中如果出现多次加锁解锁, 编译器 + JVM 会自动进行锁的粗化.
锁的粒度: 粗和细
synchronized里,代码越多,就认为锁的粒度越粗,代码越少锁的粒度越细.
实际开发过程中, 使用细粒度锁, 是期望释放锁的时候其他线程能使用锁.
但是实际上可能并没有其他线程来抢占这个锁. 这种情况 JVM 就会自动把锁粗化, 避免频繁申请释放锁.
粒度细的时候,能够并发执行的逻辑更多,更有利于充分利用多核 CPU 资源
如果粒度细的锁,被反复进行加锁解锁,可能实际效果还不如粒度粗的锁(涉及到反复的锁竞争)
例如: 给领导汇报工作,
假设领导给你安排了三个工作
完成之后,就可以给领导汇报工作了.
打第一个电话汇报一个工作,挂断.
再打第二个电话汇报第二个工作,挂断.
最后打第三个电话汇报第三个工作.
这样,领导骂不骂你,先不论. 但从效率上来讲: 不如直接一个电话全部汇报完.
1.4 相关面试题
1. 什么是偏向锁?
偏向锁不是真的加锁, 而是在锁的对象头中记录一个标记(记录该锁所属的线程). 如果没有其他线程
参与竞争锁, 那么就不会真正执行加锁操作, 从而降低程序开销. 一旦真的涉及到其他的线程竞争, 再取消偏向锁状态, 进入轻量级锁状态.
2. synchronized 实现原理是什么?
标题一的所有内容都是.
2.JUC(java.util.concurrent) 的常见类
2.1 Callable接口
Callable 是一个 interface,也是创建线程的一种方式,跟Runnable类似,相当于把线程封装了一个 “返回值”. 方便程序猿借助多线程的方式计算结果.
不使用Callable的代码示例:
① 创建一个类 Result , 包含一个 sum 表示最终结果, lock 表示线程同步使用的锁对象.
② main 方法中先创建 Result 实例, 然后创建⼀个线程 t. 在线程内部计算 1 + 2 + 3 + … + 1000.
③ 主线程同时使用 wait 等待线程 t 计算结束. (注意, 如果执行到 wait 之前, 线程 t 已经计算完了, 就不必等待了).
④ 当线程 t 计算完毕后, 通过 notify 唤醒主线程, 主线程再打印结果.
public class 不使用Callable {static class Result{public int sum = 0;public Object lock = new Object();}public static void main(String[] args) throws InterruptedException {Result result = new Result();Thread t = new Thread() {@Overridepublic void run() {int sum = 0;for (int i = 0; i <= 1000; i++) {sum += i;}synchronized (result.lock) {result.sum = sum;result.lock.notify();}}};t.start();synchronized (result.lock) {while(result.sum == 0) {result.lock.wait();}}System.out.println(result.sum);}
}
可以看到, 上述代码需要一个辅助类 Result, 还需要使用一系列的加锁和 wait notify 操作, 代码复杂,容易出错.
使用Callable的代码示例:
public class 使用Callable {public static void main(String[] args) throws ExecutionException, InterruptedException {//定义一个callable任务与runnable任务类似.Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= 1000; i++) {sum += i;}return sum;}};//不能将callable直接new成thread对象.//Thread t = new Thread(callable);//报错//正确的做法是: 借助FutureTask,以FutureTask为中介new ThreadFutureTask<Integer> futureTask = new FutureTask<>(callable);Thread t = new Thread(futureTask);t.start();//这里调用get()方法就能获取到callable里面返回的结果//由于线程是并发执行的,执行到get的时候,t线程可能还没执行完,这时get就会阻塞等待.System.out.println(futureTask.get());}
}
使用 Callable 和 FutureTask 之后, 代码简化了很多, 也不必手动写线程同步代码了.
理解Callable
Callable 和 Runnable 相对, 都是描述一个 “任务”. Callable 描述的是带有返回值的任务, Runnable描述的是不带返回值的任务.
Callable 通常需要搭配 FutureTask 来使用. FutureTask 用来保存 Callable 的返回结果. 因为Callable 往往是在另一个线程中执行的, 啥时候执行完并不确定.
FutureTask 就可以负责这个等待结果出来的共作.
理解 FutureTask
想象去吃麻辣烫. 当餐点好后, 后厨就开始做了. 同时前台会给你一张 “小票” . 这个小票就是
FutureTask. 后面我们可以随时凭这张小票去查看自己的这份麻辣烫做出来了没.
2.2 ReentrantLock
可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全
ReentrantLock 的用法:
① lock(): 加锁, 如果获取不到锁就死等.
② trylock(超时时间): 加锁, 如果获取不到锁, 等待⼀定的时间之后就放弃加锁.
③ unlock(): 解锁
ReentrantLock lock = new ReentrantLock();
-----------------------------------------
lock.lock();
try { // working
} finally { lock.unlock()
}
ReentrantLock和 synchronized 的区别:
① synchronized 是⼀个关键字, 是 JVM 内部实现的(大概率是基于 C++ 实现). ReentrantLock 是标准
库的一个类, 在 JVM 外实现的(基于 Java 实现).
② synchronized 使用时不需要手动释放锁. ReentrantLock 使用时需要手动释放. 使用起来更灵活, 但是也容易遗漏unlock.
③ synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时间就放弃.
④ synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式.
// ReentrantLock 的构造⽅法
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}
⑤ ReentrantLock 更强大的唤醒机制. synchronized 是通过 Object 的 wait / notify 实现等待-唤醒. 每次唤醒的是一个随机等待的线程. ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.
如何选择使用哪个锁?
① 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.
② 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.
③ 如果需要使用公平锁, 使用ReentrantLock.
2.3 原子类
原子类内部用的是 CAS 实现,所以性能要比加锁实现 i++ 高很多。
原子类有以下几个:
① AtomicBoolean
② AtomicInteger
③ AtomicIntegerArray
④ AtomicLong
⑤ AtomicReference
⑥ AtomicStampedReference
以 AtomicInteger 举例,常见方法有:
addAndGet(int delta); i += delta;
decrementAndGet(); --i;
getAndDecrement(); i--;
incrementAndGet(); ++i;
getAndIncrement(); i++;
2.4 线程池
虽然创建销毁线程比创建销毁进程更轻量, 但是在频繁创建销毁线程的时候还是比较低效.
线程池就是为了解决这个问题. 如果某个线程不再使用了, 并不是真正把线程销毁, 而是放到⼀个 “池子” 中, 下次如果需要用到线程就直接从池子中取, 不必通过系统来创建了.
ExecutorService 和 Executors
ExecutorService 表示一个线程池实例.
• Executors 是一个工厂类, 能够创建出几种不同风格的线程池.
• ExecutorService 的 submit 方法能够向线程池中提交若干个任务
public class Demo28 {public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(10);service.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});}
}
Executors 创建线程池的几种方式
① newFixedThreadPool: 创建固定线程数的线程池
② newCachedThreadPool: 创建线程数目动态增长的线程池.
③ newSingleThreadExecutor: 创建只包含单个线程的线程池.
④ newScheduledThreadPool: 设定延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
⑤ Executors 本质上是 ThreadPoolExecutor 类的封装.
ThreadPoolExecutor
ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定.ThreadPoolExecutor 的构造方法
理解 ThreadPoolExecutor 构造方法的参数 把创建一个线程池想象成开个公司. 每个员工相当于一个线程.
① corePoolSize: 正式员工的数量. (正式员工, 一旦录用, 永不辞退)
② maximumPoolSize: 正式员工 + 临时工的数目. (临时工: 一段时间不干活, 就被辞退).
③ keepAliveTime: 临时工允许的空闲时间.
④ unit: keepaliveTime 的时间单位, 是秒, 分钟, 还是其他值.
⑤ workQueue: 传递任务的阻塞队列
⑥ threadFactory: 创建线程的工厂, 参与具体的创建线程工作.
⑦ RejectedExecutionHandler: 拒绝策略, 如果任务量超出公司的负荷了接下来怎么处理.
⑧ AbortPolicy(): 超过负荷, 直接抛出异常.
⑨ CallerRunsPolicy(): 调用者负责处理
⑩ DiscardOldestPolicy(): 丢弃队列中最老的任务.
11. DiscardPolicy(): 丢弃新来的任务.
2.5 信号量Semaphore
信号量, 用来表示 “可用资源的个数”. 本质上就是一个计数器.
举个例子来理解信号量:
停车场门口通常会有展示牌: 剩于10个车位,表示有10个可用资源.
当有一辆车进去停,车位-1(相当于信号量的P操作).
当有 一辆车开出来,车位+1(相当于信号量的V操作).
如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源
Semaphore 的 PV 操作中的加减计数器操作都是原子的, 可以在多线程环境下直接使用.
代码示例:
① 创建 Semaphore 示例, 初始化为 4, 表示有 4 个可用资源.
② acquire 方法表示申请资源(P操作) .
③ release 方法表示释放资源(V操作).
④ 创建 20 个线程, 每个线程都尝试申请资源, sleep 1秒之后, 释放资源. 观察程序的执行效果.
public class Semaphore示例 {public static void main(String[] args) {Semaphore semaphore = new Semaphore(4);Runnable runnable = new Runnable() {@Overridepublic void run() {try {System.out.println("申请资源");semaphore.acquire();System.out.println("获取资源");Thread.sleep(1000);System.out.println("释放资源");semaphore.release();} catch (InterruptedException e) {throw new RuntimeException(e);}}};for (int i = 0; i < 20; i++) {Thread t = new Thread(runnable);t.start();}}
}
2.6 CountDownLach
CountDownLach同时等待 N 个任务执行结束. 好像跑步比赛,10个选手依次就位,哨声响才同时出发;所有选手都通过终点,才能公布成绩。
① 构造 CountDownLatch 实例, 初始化 10 表示有 10 个任务需要完成.
② 每个任务执行完毕, 都调用 latch.countDown() . 在 CountDownLatch 内部的计数器同时自减.
③ 主线程中使用 countDownLatch.await(); 阻塞等待所有任务执行完毕. 相当于计数器为 0 了
public class Demo33 {public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(10);for (int i = 0; i < 10; i++) {int id = i;Thread t = new Thread(() -> {//System.out.println("thread "+i);//不能直接用 i ,涉及到变量捕获的知识.System.out.println("thread "+id);try {Thread.sleep(1500);} catch (InterruptedException e) {throw new RuntimeException(e);}countDownLatch.countDown();});t.start();}countDownLatch.await();System.out.println("所有任务都执行完了.");}
}
2.7 相关面试题
1. 线程同步的方式有哪些?
synchronized, ReentrantLock, Semaphore 等都可以用于线程同步.
2. 为什么有了 synchronized 还需要 juc 下的 lock?
以 juc 的 ReentrantLock 为例,
① synchronized 使用时不需要手动释放锁. ReentrantLock 使⽤时需要手动释放. 使用起来更灵活,
② synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时间就放弃.
③ synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式.
④ synchronized 是通过 Object 的 wait / notify 实现等待-唤醒. 每次唤醒的是⼀个随机等待的线程.
⑤ ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.
3. AtomicInteger 的实现原理是什么?
详细看我上一篇文章: 【JavaEE初阶】多线程重点知识以及常考的面试题-多线程进阶(一) 中CAS实现原子类那一部分知识就是答案
本篇博客到这里就结束啦, 感谢观看 ❤❤❤
🐎期待与你的下一次相遇😊😊😊
相关文章:
【JavaEE初阶】多线程重点知识以及常考的面试题-多线程进阶(二)
本篇博客给大家带来的是多线程中synchronize的实现原理和JUC(java.util.concurrent) 常见类的相关知识点. 🐎文章专栏: JavaEE初阶 🚀若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的…...
Flutter PIP 插件 ---- iOS Video Call 自定义PIP WINDOW渲染内容
以下是一篇关于在 iOS 中实现画中画(PiP)功能的技术博客: iOS 画中画(PiP)功能实现指南 效果 简介 画中画(Picture in Picture, PiP)是一项允许用户在使用其他应用时继续观看视频内容的功能。本文将详细介绍如何在 iOS 应用中实现 PiP 功能,包括自定义内容渲染和…...
【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——4G模块ME3630测试
1)实验平台:正点原子ATK-DLMP257B开发板 2)浏览产品:https://www.alientek.com/Product_Details/135.html 3)全套实验源码手册视频下载:正点原子资料下载中心 文章目录 第四章 ATK-DLMP257B功能测试——4G模…...
高并发内存池(四):内存释放原理与实现
前言:经过前3期的攻坚,我们已完整实现了内存动态申请的核心模块。接下来将进入关键阶段——内存释放机制的理解与实现,这是构建完整 高并发内存池 的最后一块技术拼图。该模块完成后,项目主体架构将基本成型(达90%&…...
【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——EEPROM、SPI FLASH测试 #AT24C64 #W25Q128
1)实验平台:正点原子ATK-DLMP257B开发板 2)浏览产品:https://www.alientek.com/Product_Details/135.html 3)全套实验源码手册视频下载:正点原子资料下载中心 文章目录 第四章 ATK-DLMP257B功能测试——EEP…...
《突破控件限制:用Qt绘图API解锁高级界面定制能力》
一、基本概念 虽然 Qt 已经内置了很多的控件,但是不能保证现有控件就可以应对所有场景。很多时候我们需要更强的 “自定制” 能力。 Qt 提供了画图相关的 API,允许我们在窗口上绘制任意的图形形状来完成更复杂的界面设计 所谓的 “控件” 本质上也是通…...
MyBatis-Plus 中BaseMapper接口是如何加速微服务内部开发的?
假设我们有一个简单的微服务项目,需要对 User 实体进行基本的数据库操作。 场景一:使用原生 MyBatis 的开发流程 (作为对比) 定义实体类 (Entity): // package com.yourcompany.usermicroservice.entity; public class User {private Long id;private S…...
AIGC-十款数据分析类智能体完整指令直接用(DeepSeek,豆包,千问,Kimi,GPT)
Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列AIGC(GPT、DeepSeek、豆包、千问、Kimi)👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资…...
鸿蒙应用(医院陪诊系统)开发篇1·主页面的tabs布局
引言: 首先可在鸿蒙开发者官网找到DevEco Studio的安装教程。 安装好了之后,我们开始开发。 知识点: tabs布局,鸿蒙官方文档地址:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-contai…...
【大模型】DeepSeek + Coze 打造个人专属AI智能体使用详解
目录 一、前言 二、AI智能体介绍 2.1 什么是AI智能体 2.2 AI智能体核心能力 2.3 AI智能应用场景 三、coze 介绍 3.1 coze是什么 3.1.1 平台概述 3.1.2 平台适用人群 3.2 平台核心功能 3.3 coze可以做什么 3.4 为什么选择coze 四、coze 搭建AI智能体操作实践 4.1 搭…...
7.(vue3.x+vite)弹性布局(flex布局)
1:效果截图 2:代码实现 <template><span style="font-size: 20px">右边固定,左边自适应</span><div class=<...
Python基础总结(五)之字典
文章目录 一、字典的格式二、字典的操作2.1 增加项2.1.1 直接增加项2.1.1 formkeys方法 2.2 删除项2.2.1 clear()方法2.2.2 pop()方法 2.3 修改项2.3.1 直接修改2.3.2 update()方法 2.4 查找项2.4.1 get()方法2.4.2 直接查询2.4.3 items()方法2.4.4 keys()方法2.4.5 values()方…...
MCP简介:重构人机交互底层逻辑
在人工智能技术飞速发展的今天,大语言模型(LLM)的应用场景正不断拓展,但模型与外部系统之间的连接方式却成为制约其潜力发挥的关键瓶颈。为了解决这一难题,由AI领域顶尖公司Anthropic(Claude模型背后的开发…...
LangChain缓存嵌入技术完全指南:CacheBackedEmbedding原理与实践(附代码示例)
一、嵌入缓存技术背景与应用场景 1.1 为什么需要嵌入缓存? 算力消耗问题:现代嵌入模型(如text-embedding-3-small)单次推理需要约0.5-1秒/文本 资源浪费现状:实际业务中约30%-60%的文本存在重复计算 成本压力&#…...
国产DPU芯片+防火墙,能否引领网络安全新跨越?
近日,国内首款搭载国产DPU芯片的800Gbps下一代防火墙——中科网威NSFW - 12000正式发布,引发行业广泛关注。 国产DPU芯片与防火墙的结合,正在推动网络安全领域实现技术突破与体系升级。以下从技术特性、应用场景和产业价值三个维度分析其引领…...
Spark-SQL与Hive的连接及数据处理全解析
Spark-SQL与Hive的连接及数据处理全解析 在大数据处理领域,Spark-SQL和Hive都是重要的工具。今天就来聊聊Spark-SQL如何连接Hive以及相关的数据处理操作。 Spark-SQL连接Hive有多种方式。内嵌Hive虽然使用简单,直接就能用,但在实际生产中…...
ArcGIS Desktop使用入门(四)——9版本与10版本区别
系列文章目录 ArcGIS Desktop使用入门(一)软件初认识 ArcGIS Desktop使用入门(二)常用工具条——标准工具 ArcGIS Desktop使用入门(二)常用工具条——编辑器 ArcGIS Desktop使用入门(二&#x…...
使用 chromedriver 实现网络爬虫【手抄】
1、引用 selenium 包 <dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.29.0</version> </dependency> <dependency><groupId>org.seleniumhq.seleniu…...
ERROR: Unknown host CPU architecture: arm64
1、先检查NDK版本是否支持arm64,NDK版本21.3以上 2、检查gradle中,有关NDK版本是否设置arm64-v8a 例如: ndkVersion "21.4.7075529"ndk {abiFilters "armeabi-v7a","arm64-v8a"} local.properties中&#x…...
从 SQL2API 到 Text2API:开启数据应用开发的新征程
在技术革新浪潮的席卷下,数据应用开发领域正经历着深刻变革。曾经,构建数据 API 需要开发者具备扎实的数据库知识和编程技能,手动编写复杂的 SQL 查询与 API 代码,这一过程不仅耗时费力,还将众多非技术人员阻挡在数据应…...
OpenCv高阶(四)——角点检测
一、角点检测 在计算机视觉中,角点检测是识别图像中局部区域(角点)的关键技术,这些区域通常是两条或多条边缘的交点,具有丰富的结构信息,常用于图像匹配、跟踪、三维重建等任务。 Harris角点检测算法是一…...
centos8 部署 openstack
在 CentOS 8 上部署 OpenStack 是一个复杂的过程,涉及多个组件的安装和配置。OpenStack 是一个开源的云计算平台,它提供了基础设施即服务(IaaS)的功能。下面我将指导你通过基本的步骤来部署 OpenStack。 前提条件 系统要求&#…...
智能云图库-8-AI编辑
一、基础图片编辑 需求分析 在日常的图片管理中,用户经常需要对图片进行简单处理,比如裁剪多余部分、旋转图片、放大缩小尺寸等。 因此,我们首先要引入基础图片编辑功能,帮助用户快速完成以下操作: 裁剪&#…...
libwebsocket建立服务器需要编写LWS_CALLBACK_ADD_HEADERS事件处理
最近在使用libwebsocket,感觉它搭建Http与websocket服务器比较简单,不像poco库那么庞大,但当我使用它建立websocket服务器后,发现websocket客户端连接一直没有连接成功,不知道什么原因,经过一天的调试&…...
L1-002 打印沙漏
L1-002 打印沙漏 - 团体程序设计天梯赛-练习集 (pintia.cn) 本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印 ************ *****所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中心对齐&#…...
JSP技术入门指南【一】利用IDEA从零开始搭建你的第一个JSP系统
Jsp技术入门指南【一】利用IDEA从零开始搭建你的第一个JSP系统 前言一、什么是JSP1.1 JSP是干什么的?1.2 JSP与Servlet的关系是什么? 二、在Idea中创建第一个JSP系统三、JSP和HTML的差别3.1 格式区别3.2 注释区别 前言 在前面的内容中,我们已…...
NLP高频面试题(四十四)——RLHF过程中的马尔科夫决策过程及对话场景MDP设计
什么是马尔科夫决策过程(MDP)? 马尔科夫决策过程(MDP)是描述序贯决策问题的数学框架,由五元组( (S, A, P, R, \gamma) )组成,其中: (S):状态集合,描述环境的所有可能状态。(A):动作集合,描述智能体可以采取的所有可能动作。(P):状态转移概率函数,表示从一个状态…...
青少年编程与数学 02-016 Python数据结构与算法 24课题、密码学算法
青少年编程与数学 02-016 Python数据结构与算法 24课题、密码学算法 课题摘要:一、对称加密算法AES(高级加密标准)DES(数据加密标准)3DES(三重数据加密标准) 二、非对称加密算法RSAECC(椭圆曲线…...
艺术字体AI生成阿里云WordArt锦书、通义万相、SiliconFlow、Pillow+OpenCV本地生成艺术字体
基于您的需求,结合最新API技术和搜索结果,以下是Python调用主流艺术字API的代码案例及对应充值链接方案: 一、大厂API服务(付费方案) 1. 阿里云WordArt锦书API # 文字纹理生成(需安装dashscopeÿ…...
GIS开发笔记(7)结合osg及osgEarth实现不同高度下的三个圆形区域形成的三维覆盖轮廓区域绘制
一、实现效果如下: 二、实现原理 输入四个坐标点,第一个点为中心点,第二三四个点分别以中心点与地面垂线所在线上的各自高度水平面的交点为圆心旋转360进行圆形区域绘制,然后将三个不同高度上的圆形区域进行结合形成三维覆盖区域…...
解决 Kubernetes 调度器启动报错:缺少 Bind 插件
解决 Kubernetes 调度器启动报错:缺少 Bind 插件 问题描述 在启动自定义 Kubernetes 调度器时,出现了以下错误: I0416 17:08:41.420188 68788 configfile.go:57] "KubeSchedulerConfiguration v1beta2 is deprecated in v1.25, wil…...
Spring Batch 专题系列(六):并行处理与性能优化
1. 引言 在上一篇文章中,我们学习了 Spring Batch 的错误处理机制(Skip、Retry、Restart 和 Listener),掌握了如何提升作业的健壮性。随着数据量的增加,批处理任务的性能成为关键挑战。Spring Batch 提供了强大的并行处理功能,包括多线程 Step、分区(Partitioning)和并…...
WPF 从Main()方法启动
1.去掉App.xaml StartupUri“MainWindow.xaml” 只会让App.g.cs 不生成这行代码,但是还是会生成的App.g.cs文件中生成Main方法 this.StartupUri new System.Uri("MainWindow.xaml", System.UriKind.Relative);默认的App.xaml的生成操作是 应用程序定义…...
记录小程序第一次调用Api,基于腾讯云Serverless函数,实现小程序的成功接入api,以及数据调用
目录 创建腾讯云个人账户新建severless应用建立函数URL小程序中调用api示例 创建腾讯云个人账户 百度搜索即可,并注册 新建severless应用 作者以github下载的某Api为例,这里不展示具体Api,只关注操作即可,相信都是互通的 在腾…...
「数据可视化 D3系列」入门第四章:DOM操作与元素管理
DOM操作与元素管理 核心技能:元素选择与操作一、元素选择:精准定位DOM节点基础选择方法对比进阶选择技巧 二、元素插入:动态构建DOM结构append() vs insert()实际应用示例 三、元素删除:优雅移除DOM节点删除操作最佳实践 四、内容…...
突发重磅消息!!!CVE项目将被取消?
突发重磅消息!!!CVE项目将被取消?突发!来自可靠消息来源。MITRE 对 CVE 项目的支持将于明天到期。附件信件已发送给 CVE 董事会成员。https://mp.weixin.qq.com/s/N3qkiHaDfzDuBMK3JbBCjw...
基于uniapp的鸿蒙APP大数据量性能优化
文章目录 一、问题诊断与性能瓶颈分析1.1 大数据场景下的典型性能问题1.2 性能监测工具使用1.2.1 HBuilderX内置分析器1.2.2 鸿蒙DevEco工具链1.2.3 自制性能埋点 二、数据加载优化方案2.1 分页加载实现(带错误重试机制)2.2 数据流优化策略2.2.1 数据压缩…...
【算法】归并排序
算法系列七:归并排序 一、归并排序的递归探寻 1.思路 2.搭建 2.1设计过掉不符情况(在最底层时) 2.2查验能实现基础排序(在最底层往上点时) 2.3跳转结果继续往上回搭 3.实质 4.实现 二、递归的调用栈 1.递归的…...
【JavaScript】二十三、M端事件 + 轮播图Swiper插件
文章目录 1、M端事件2、swiper插件2.1 插件2.2 轮播图插件Swiper的使用 3、案例:学生信息表 1、M端事件 移动端有一个独有的事件:触屏事件 touch(也称触摸事件),Android 和 IOS 都有,touch 对象代表一个触摸点。触摸点可能是一根…...
【Spring】DI(依赖注入)详解:属性注入@Autowired(超详细)、构造方法注入、Setter注入
1.DI(依赖注入)介绍 1.1DI是什么? DI(Dependency Injection,依赖注入) 是 Spring 框架中实现 IoC(控制反转)的一种核心机制。如果说 IoC 是一种设计思想,告诉我们“把控…...
Spring Boot 中配置 Redis 连接池的详细
目录 一、添加依赖二、配置 Redis 连接池(一)通过 Java 配置类(二)通过 application.properties 文件 三、测试 Redis 操作四、总结 一、添加依赖 在 pom.xml 文件中添加以下依赖: <dependencies><dependen…...
系统架构设计师:系统架构概述案例分析与简答题、详细解析与评分要点
10道系统架构概述知识体系案例分析与简答题,涵盖架构设计原则、质量属性、演化过程、评估方法等核心考点,并附详细解析与评分要点: 一、案例分析题(5题) 1. 电商系统高并发场景下的架构设计 背景:某电商平…...
关于系统架构思考,如何设计实现系统的高可用?
绪论、系统高可用的必要性 系统高可用为了保持业务连续性保障,以及停机成本量化,比如在以前的双十一当天如果出现宕机,那将会损失多少钱?比如最近几年Amazon 2021年30分钟宕机损失$5.6M。当然也有成功的案例,比如异地…...
阿里云短信服务与ASP.NET对接实例
准备工作 注册阿里云账号并开通阿里大于(现称"阿里云短信服务")服务 获取AccessKey ID和AccessKey Secret 申请短信签名和短信模板并审核通过 ASP.NET Web项目集成步骤 1. 安装阿里云SDK 通过NuGet包管理器安装阿里云短信服务SDK: Install-Package…...
【含文档+PPT+源码】基于微信小程序健康管理之健身房管理系统的设计与实现
课程目标: 教你从零开始部署运行项目,学习环境搭建、项目导入及部署,含项目源码、文档、数据库、软件等资料 课程简介: 本课程演示的是一款基于微信小程序健康管理之健身房管理系统的设计与实现,主要针对计算机相关…...
微信小程序转为App实践篇 FinClip
参考下面链接先 开始实践 微信小程序转为App并上架应用市场_微信小程序生成app-CSDN博客 首先在FinClip 官网上下载应用 小程序开发工具下载_小程序sdk下载资源-FinClip资源下载|泰坪小程序开放平台 下载到本地安装 打开导入自己的小程序项目;导入时会解析自己的…...
Qt/C++学习系列之QTreeWidget的简单使用记录
Qt/C学习系列之QTreeWidget的简单使用记录 前言1布局1.1布局要求1.2布局代码 2代码设计2.1整体勾选2.2勾选项确认 总结 前言 自己练手的项目中,需要对多个不同层级的选项进行勾选操作,而想到简洁点的操作方式就是使用QTreeWidget进行布局与应用。这里简…...
标易行项目redis内存中放哪些数据
结合你的项目经验,以下是 标易行投标服务平台 中 Redis 内存存储的核心数据类型及具体应用场景分析: 1. 用户订阅配置与实时推送 场景需求:用户订阅招标商机后,系统需实时推送符合订阅条件(如行业、区域、关键词)的标讯。Redis 存储数据: 订阅规则缓存:以 Hash 存储用户…...
redis 放置序列化的对象,如果修改对象,需要修改版本号吗?
在 Redis 中存储序列化对象时,如果修改了对象的类结构(例如增删字段、修改字段类型或顺序),是否需要修改版本号取决于序列化协议的兼容性策略和业务场景的容错需求。以下是详细分析: 1. 为什么需要考虑版本号? 序列化兼容性问题: 当对象的类结构发生变化时,旧版本的序列…...
MySQL——流程控制
一、IF条件语句 语法 IF condition THENstatements; ELSEIF condition THENstatements; ELSEstatements; END IF; 判断成绩等级 # 判断成绩等级 # 输入学生的编号,取出学生的第一门课,然后判断当前的课程的等级 drop procedure if exists p2; delimiter $$ crea…...