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

JUC并发编程:共享模型之管程

一、共享带来的问题

(1)Java的体现

两个线程对初始值为 0 的静态变量一个做自增,一个做自减,各做 5000 次,结果是 0 吗?

(2)问题分析

  1. 以上的结果可能是正数负数。为什么呢?因为 Java 中对静态变量的自增,自减并不是原子操作,要彻底理解,必须从字节码来进行分析
  2. 例如对于 i++ 而言(i 为静态变量),实际会产生如下的 JVM 字节码指令
  3. 而对应 i-- 也是类似:
  4. 而 Java 的内存模型如下,完成静态变量的自增,自减需要在主存和工作内存中进行数据交换:
  5. 如果是单线程以上 8 行代码是顺序执行(不会交错)没有问题:
  6. 但多线程下这 8 行代码可能交错运行
    1. 出现负数的情况:
    2. 出现正数的情况:

(3)临界区 Critical Section

  1. 一个程序运行多个线程本身是没有问题的
  2. 问题出在多个线程访问共享资源
    1. 多个线程读共享资源其实也没有问题
    2. 在多个线程对共享资源读写操作时发生指令交错,就会出现问题
  3. 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区

(4)竞态条件 Race Condition

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件

二、synchronized 解决方案

(1)* 应用之互斥

  1. 竞态条件解决方案分类
    1. 阻塞式:如 synchronized、Lock
    2. 非阻塞式:如原子变量
  2. synchronized(对象锁)的作用
    1. 以互斥方式工作,同一时刻最多仅一个线程能持有 “对象锁”
    2. 其他线程尝试获取锁时会被阻塞,确保持有锁的线程安全执行临界区代码,规避线程上下文切换引发的问题
  3. synchronized 的互斥与同步区别
    1. 互斥:保证临界区不出现竞态条件,同一时刻仅一个线程可执行临界区代码
    2. 同步:因线程执行顺序、先后不同,需让某个线程等待其他线程运行到特定节点

(2)synchronized

  1. 语法
  2. 解决
  3. 你可以做这样的类比:
    1. synchronized(对象)中的对象,可以想象为一个房间(room),有唯一入口(门)房间只能一次进入一人 进行计算,线程 t1,t2 想象成两个人
    2. 当线程 t1 执行到 synchronized(room) 时就好比 t1 进入了这个房间,并锁住了门拿走了钥匙,在门内执行 count++ 代码
    3. 这时候如果 t2 也运行到了 synchronized(room) 时,它发现门被锁住了,只能在门外等待,发生了上下文切换,阻塞住了
    4. 这中间即使 t1 的 cpu 时间片不幸用完,被踢出了门外(不要错误理解为锁住了对象就能一直执行下去哦),这时门还是锁住的,t1 仍拿着钥匙,t2 线程还在阻塞状态进不来,只有下次轮到 t1 自己再次获得时间片时能开门进入
    5. 当 t1 执行完 synchronized{} 块内的代码,这时候才会从 obj 房间出来并解开门上的锁,唤醒 t2 线程把钥匙给他。t2 线程这时才可以进入 obj 房间,锁住了门拿上钥匙,执行它的 count-- 代码

(3)思考

  1. synchronized 实际是用对象锁保证了临界区内代码的原子性,临界区内的代码对外是不可分割的,不会被线程切换所打断
  2. 为了加深理解,请思考下面的问题
    1. 如果把 synchronized(obj) 放在 for 循环的外面,如何理解?-- 原子性
    2. 如果 t1 synchronized(obj1) 而 t2 synchronized(obj2) 会怎样运作?-- 锁对象
    3. 如果 t1 synchronized(obj) 而 t2 没有加会怎么样?如何理解?-- 锁对象

(4)面向对象改进

把需要保护的共享变量放入一个类

三、方法上的synchronized

(1)不加 synchronized 的方法

不加 synchronzied 的方法就好比不遵守规则的人,不去老实排队(好比翻窗户进去的)

(2)所谓的“线程八锁”

3.2.1情况一

3.2.2情况二

3.2.3情况三

3.2.4情况四

3.2.5情况五

3.2.6情况六

3.2.7情况七

3.2.8情况八

四、变量的线程安全分析

(1)成员变量和静态变量是否线程安全?

  1. 如果它们没有共享,则线程安全
  2. 如果它们被共享了,根据它们的状态是否能够改变,又分两种情况
    1. 如果只有读操作,则线程安全
    2. 如果有读写操作,则这段代码是临界区,需要考虑线程安全

(2)局部变量是否线程安全?

  1. 局部变量是线程安全的
  2. 但局部变量引用的对象则未必
    1. 如果该对象没有逃离方法的作用访问,它是线程安全
    2. 如果该对象逃离方法的作用范围需要考虑线程安全

(3)局部变量线程安全分析

(4)常见线程安全类

  1. 常见线程安全类
    1. String
    2. Integer
    3. StringBuffer
    4. Random
    5. Vector
    6. Hashtable
    7. java.util.concurrent 包下的类
  2. 这里说它们是线程安全的是指,多个线程调用它们同一个实例的某个方法时,是线程安全的。也可以理解为
  3. 注意
    1. 它们的每个方法是原子的
    2. 但注意它们多个方法的组合不是原子的,见后面分析

(5)线程安全类方法的组合

(6)不可变类线程安全性

  1. String、Integer 等都是不可变类,因为其内部的状态不可以改变,因此它们的方法都是线程安全的
  2. 有同学或许有疑问,String 有 replace,substring 等方法【可以】改变值啊,那么这些方法又是如何保证线程安全的呢?

(7)实例分析

  1. 例1
  2. 例2
  3. 例3
  4. 例4
  5. 例5
  6. 例6
  7. 例7
  8. 例8

五、Monitor概念

(1)Java对象头

  1. 32位虚拟机为例
  2. 64位虚拟机Mark Word:

(2)重量级锁(Monitor)、轻量级锁、偏向锁

六、wait notify

(1)为什么需要wait

  1. 多线程场景下,线程获取锁后若条件不满足(如等待资源),调用wait()释放锁,进入等待队列(WaitSet),避免占用锁阻塞其他线程
  2. 其他线程可竞争锁执行任务。当条件满足,通过notify()唤醒等待线程
  3. 被唤醒的线程离开等待队列,重新竞争锁(进入竞争队列 EntrySet),获取锁后继续操作
  4. 这一机制通过释放锁、唤醒、重新竞争,提升多线程资源利用效率

(2)API介绍

  1. obj.wait() 让进入 object 监视器的线程到 waitSet 等待
  2. obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒
  3. obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒
  4. 它们都是线程之间进行协作的手段,都属于 Object 对象的方法。必须获得此对象的锁,才能调用这几个方法
  5. wait() 方法会释放对象的锁,进入 WaitSet 等待区,从而让其他线程就机会获取对象的锁。无限制等待,直到notify 为止
  6. wait(long n) 有时限的等待, 到 n 毫秒后结束等待,或是被 notify

(3)wait notify的正确姿势

sleep(long n)wait(long n) 的区别

  1. sleep 是 Thread 方法,而 wait 是 Object 的方法
  2. sleep 不需要强制和 synchronized 配合使用,但 wait 需要 和 synchronized 一起用
  3. sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
  4. 它们 状态 TIMED_WAITING

  1. 锁释放与阻塞问题:仅用 sleep 不释放锁,会阻塞其他线程,降低效率
  2. notify 缺陷notify 随机唤醒线程,可能无法精准唤醒目标线程,出现 “虚假唤醒”(唤醒非预期线程)
  3. notifyAll 与条件判断:notifyAll 可唤醒所有等待线程,但 if + wait 仅判断一次条件,条件不成立时无法重新验证
  4. 正确方案:用 while + wait 循环判断条件,配合 notifyAll,确保每次唤醒后重新检查条件,避免逻辑错误
    import lombok.extern.slf4j.Slf4j;@Slf4j
    public class CorrectWaitNotifyDemo {static final Object room = new Object();static boolean hasCigarette = false;static boolean hasTakeout = false;public static void main(String[] args) {// 小南线程:等待烟new Thread(() -> {synchronized (room) {while (!hasCigarette) { // while循环检查条件log.debug("没烟,先歇会!");try {room.wait(); // 释放锁,进入等待} catch (InterruptedException e) {e.printStackTrace();}}log.debug("有烟了,可以开始干活");}}, "小南").start();// 小女线程:等待外卖new Thread(() -> {synchronized (room) {while (!hasTakeout) { // while循环检查条件log.debug("没外卖,先歇会!");try {room.wait(); // 释放锁,进入等待} catch (InterruptedException e) {e.printStackTrace();}}log.debug("有外卖了,可以开始干活");}}, "小女").start();try {Thread.sleep(1000); // 模拟时间} catch (InterruptedException e) {e.printStackTrace();}// 送外卖线程:触发条件并唤醒new Thread(() -> {synchronized (room) {hasTakeout = true;log.debug("外卖到了噢!");room.notifyAll(); // 唤醒所有等待线程}}, "送外卖的").start();try {Thread.sleep(1000); // 模拟时间} catch (InterruptedException e) {e.printStackTrace();}// 送烟线程:触发条件并唤醒new Thread(() -> {synchronized (room) {hasCigarette = true;log.debug("烟到了噢!");room.notifyAll(); // 唤醒所有等待线程}}, "送烟的").start();}
    }

七、Park & Unpark

(1)基本使用

  1. 它们是 LockSupport 类中的方法
  2. park unpark
  3. unparkpark

(2)特点

与 Object 的 wait & notify 相比

  1. wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
  2. park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll 是唤醒所有等待线程,就不那么【精确】
  3. park & unpark 可以先 unpark,而 wait & notify 不能先 notify

八、重新理解线程状态转换

假设有线程 Thread t

(1)情况 1 NEW --> RUNNABLE

当调用 t.start() 方法时,由 NEW --> RUNNABLE

(2)情况 2 RUNNABLE <--> WAITING

t 线程用 synchronized(obj) 获取了对象锁后

  1. 调用 obj.wait() 方法时,t 线程从 RUNNABLE --> WAITING
  2. 调用 obj.notify(),obj.notifyAll(),t.interrupt() 时
    1. 竞争锁成功,t 线程从 WAITING --> RUNNABLE
    2. 竞争锁失败,t 线程从 WAITING --> BLOCKED

(3)情况 3 RUNNABLE <--> WAITING

  1. 当前线程调用 t.join() 方法时,当前线程从 RUNNABLE --> WAITING(注意是当前线程在 t 线程对象的监视器上等待)
  2. t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 WAITING --> RUNNABLE

(4)情况 4 RUNNABLE <--> WAITING

  1. 当前线程调用 LockSupport.park() 方法会让当前线程从 RUNNABLE --> WAITING
  2. 调用 LockSupport.unpark(目标线程) 或调用了线程的 interrupt(),会让目标线程从 WAITING --> RUNNABLE

(5)情况 5 RUNNABLE <--> TIMED_WAITING

t 线程用 synchronized(obj) 获取了对象锁后

  1. 调用 obj.wait(long n) 方法时,t 线程从 RUNNABLE --> TIMED_WAITING
  2. t 线程等待时间超过了 n 毫秒,或调用 obj.notify(),obj.notifyAll(),t.interrupt() 时
    1. 竞争锁成功,t 线程从 TIMED_WAITING --> RUNNABLE
    2. 竞争锁失败,t 线程从 TIMED_WAITING --> BLOCKED

(6)情况 6 RUNNABLE <--> TIMED_WAITING

  1. 当前线程调用 t.join(long n) 方法时,当前线程从 RUNNABLE --> TIMED_WAITING注意是当前线程在 t 线程对象的监视器上等待
  2. 当前线程等待时间超过了 n 毫秒,或 t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 TIMED_WAITING --> RUNNABLE

(7)情况 7 RUNNABLE <--> TIMED_WAITING

  1. 当前线程调用 Thread.sleep(long n),当前线程从 RUNNABLE --> TIMED_WAITING
  2. 当前线程等待时间超过了 n 毫秒,当前线程从 TIMED_WAITING --> RUNNABLE

(8)情况 8 RUNNABLE <--> TIMED_WAITING

  1. 当前线程调用 LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis) 时,当前线程从 RUNNABLE --> TIMED_WAITING
  2. 调用 LockSupport.unpark(目标线程) 或调用线程的 interrupt(),或是等待超时,让目标线程从 TIMED_WAITING--> RUNNABLE

(9)情况 9 RUNNABLE <--> BLOCKED

  1. t 线程用 synchronized(obj) 竞争对象锁失败,从 RUNNABLE --> BLOCKED
  2. 持 obj 锁线程的同步代码块执行完,唤醒该对象上所有 BLOCKED 线程重新竞争。若 t 线程竞争成功,从 BLOCKED --> RUNNABLE,其他失败线程仍 BLOCKED

(10)情况 10 RUNNABLE <--> TERMINATED

当前线程所有代码运行完毕,进入 TERMINATED

九、多把锁

  1. 问题:单一对象锁下,不同无关功能(如睡觉、学习)会互相阻塞,降低并发
  2. 解决:使用多把对象锁(如studyRoom、bedRoom),让不同功能操作各自的锁,避免互相干扰,提升程序并发度
  3. 锁的粒度细分
    1. 好处,是可以增强并发度
    2. 坏处,如果一个线程需要同时获得多把锁,就容易发生死锁

十、活跃性

(1)死锁

  1. 死锁成因线程同时获取多把锁时,若线程间交叉持有锁且循环等待(如 t1 持 A 锁等 B 锁,t2 持 B 锁等 A 锁),会引发死锁
  2. 示例本质:多线程对不同对象锁的交叉获取循环等待,导致程序无法继续执行

(2)定位死锁

  1. 检测死锁可以使用 jconsole工具,或者使用 jps 定位进程 id再用 jstack 定位死锁
  2. 避免死锁要注意加锁顺序
  3. 另外如果由于某个线程进入了死循环,导致其它线程一直等待,对于这种情况 linux 下可以通过 top 先定位到 CPU 占用高的 Java 进程,再利用 top -Hp 进程id 来定位是哪个线程,最后再用 jstack 排查

(3)活锁

活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束

(4)饥饿

  1. 很多教程中把饥饿定义为,一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束,饥饿的情况不易演示,讲读写锁时会涉及饥饿问题
  2. 下面我讲一下我遇到的一个线程饥饿的例子,先来看看使用顺序加锁的方式解决之前的死锁问题
  3. 顺序加锁的解决方案

十一、ReentrantLock

相对于 synchronized 它具备如下特点

  1. 可中断
  2. 可以设置超时时间
  3. 可以设置为公平锁
  4. 支持多个条件变量

与 synchronized 一样,都支持可重入 

(1)可重入

  1. 可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁
  2. 如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住

(2)可打断

(3)锁超时

  1. 立刻失败
  2. 超时失败
  3. 使用 tryLock 解决哲学家就餐问题

(4)公平锁

  1. ReentrantLock 默认是不公平的
  2. 改为公平锁

(5)条件变量

  1. synchronized 中也有条件变量,就是我们讲原理时那个 waitSet 休息室,当条件不满足时进入 waitSet 等待
  2. ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比
    1. synchronized 是那些不满足条件的线程都在一间休息室等消息
    2. ReentrantLock 支持多间休息室,有专门等烟的休息室、专门等早餐的休息室、唤醒时也是按休息室来唤 醒
  3. 使用要点
    1. await 前需要获得锁
    2. await 执行后,会释放锁,进入 conditionObject 等待
    3. await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁
    4. 竞争 lock 锁成功后,从 await 后继续执行
  4. 例子:

相关文章:

JUC并发编程:共享模型之管程

一、共享带来的问题 &#xff08;1&#xff09;Java的体现 两个线程对初始值为 0 的静态变量一个做自增&#xff0c;一个做自减&#xff0c;各做 5000 次&#xff0c;结果是 0 吗&#xff1f; &#xff08;2&#xff09;问题分析 以上的结果可能是正数、负数、零。为什么呢…...

Java构造方法详解:从入门到实战

目录 一、什么是构造方法&#xff1f; 二、构造方法的作用 三、构造方法分类与使用 1. 默认构造方法 2. 有参构造方法 3. 构造方法重载 四、注意事项&#xff08;避坑指南&#xff09; 五、经典面试题解析 六、实战应用场景 七、总结 一、什么是构造方法&#xff1f; …...

Uniapp 字体加载问题(文件本地存储)

项目场景&#xff1a; 在最近公司开发一款小程序&#xff0c;但是小程序的文字需要用艺术字&#xff0c;就是那种不能用切图绕开的那种&#xff01; 问题描述 我们在使用uni.loadfontface Api请求数据字体文件的时候总是会报错&#xff0c;就是那种网上也找不到解决方法的那种…...

HTML 新手入门:从零基础到搭建第一个静态页面(一)

开启 HTML 学习之旅 在互联网的广袤世界中&#xff0c;网页是我们与信息交互的主要窗口。而 HTML&#xff0c;作为构建网页的基石&#xff0c;就像是搭建房屋的砖块&#xff0c;是网页开发中不可或缺的基础。无论你是对网页开发充满好奇的小白&#xff0c;还是渴望系统学习前端…...

使用multiprocessing实现进程间共享内存

在 Python 中,可以使用多种方法来实现几个进程之间的通信。 简单消息传递:使用 multiprocessing.Queue 或 multiprocessing.Pipe。 共享简单数据:使用 multiprocessing.Value 或 multiprocessing.Array。 共享复杂数据:使用 multiprocessing.Manager。 进程间信号控制:使用…...

在IDEA中连接达梦数据库:详细配置指南

达梦数据库&#xff08;DM Database&#xff09;作为国产关系型数据库的代表&#xff0c;广泛应用于企业级系统开发。本文将详细介绍如何在IntelliJ IDEA中配置并连接达梦数据库&#xff0c;助力开发者高效完成数据库开发工作。 准备工作 1. 下载达梦JDBC驱动 访问达梦官方资…...

docker无法正常拉取镜像问题的解决

目录 1.前言 2.解决方案 1.前言 安装docker后拉取镜像&#xff0c;遇见了如下问题&#xff1a; Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded whil…...

如何在保持安全/合规的同时更快地构建应用程序:DevOps 指南

随着敏捷思维方式的兴起&#xff0c;开发和 DevOps 团队都面临着持续的压力&#xff0c;他们需要以迭代方式缩短发布周期并加快部署速度&#xff0c;以满足不断增长的客户期望。随着这种对速度的追求越来越强烈&#xff0c;维护安全性和合规性标准的复杂性也随之增加。 当今 D…...

SQL Server查询优化

最常用&#xff0c;最有效的数据库优化方式 查询语句层面 避免全表扫描 使用索引&#xff1a;确保查询条件中的字段有索引。例如&#xff0c;查询语句 SELECT * FROM users WHERE age > 20&#xff0c;若 age 字段有索引&#xff0c;数据库会利用索引快速定位符合条件的记…...

iOS底层原理系列04-并发编程

在移动应用开发中&#xff0c;流畅的用户体验至关重要&#xff0c;而并发编程是实现这一目标的关键技术。本文将深入探讨iOS平台上的并发编程和多线程架构&#xff0c;帮助你构建高性能、响应迅速的应用程序。 1. iOS线程调度机制 1.1 线程本质和iOS线程调度机制 线程是操作…...

企业数字化转型数据治理解决方案(119页PPT)(文末有下载方式)

资料解读&#xff1a;企业数字化转型数据治理解决方案 详细资料请看本解读文章的最后内容。 在当今数字化时代&#xff0c;数据已经成为企业最宝贵的资产之一。然而&#xff0c;随着数据量的激增和数据来源的多样化&#xff0c;如何有效管理和利用这些数据成为了企业面临的一…...

git报错:“fatal:refusing to merge unrelated histories“

新建仓库&#xff0c;克隆本地项目到新仓库&#xff0c;首次同步本地已提交的代码到远程时&#xff0c;报错&#xff1a;"fatal:refusing to merge unrelated histories" 。 报错意思是&#xff1a;致命的&#xff1a;拒绝合并无关的历史。 一、问题背景&#xff…...

Jmeter下载及环境配置

Jmeter下载及环境配置 java环境变量配置配置jdk环境变量检查是否配置成功JMeter下载 java环境变量配置 访问地址&#xff1a; https://www.oracle.com/cn/java/technologies/downloads/ 注意&#xff1a;需要自己注册账号 下载完成&#xff0c;解压后的目录为&#xff1a; …...

K8S学习之基础二十四:k8s的持久化存储之pv和pvc

K8S的存储之pv和pvc 在 Kubernetes (k8s) 中&#xff0c;持久化存储是通过 PersistentVolume (PV) 和 PersistentVolumeClaim (PVC) 来实现的。PVC 是用户对存储资源的请求&#xff0c;而 PV 是集群中的实际存储资源。PVC 和 PV 的关系类似于 Pod 和 Node 的关系。 Persisten…...

1.5、Java构造方法重载

构造方法重载的实现 &#xff08;1&#xff09;定义多个构造方法 class Person {private String name;private int age;// 无参构造方法public Person() {this.name "Unknown";this.age 0;}// 带一个参数的构造方法public Person(String name) {this.name name;…...

领域驱动设计(DDD)技术分享:从三层架构到DDD的进化之旅

一、开篇话&#xff1a;我们为什么要聊DDD&#xff1f; 如果你像我一样有着Java开发背景&#xff0c;那Spring的三层架构可能是你的老朋友了。Controller-Service-DAO这种模式简直就像我们编程的"家常便饭"。但是&#xff0c;随着业务越来越复杂&#xff0c;你是否也…...

LeetCode - #227 基于 Swift 实现基本计算器

摘要 在这篇文章中&#xff0c;我们将实现一个基于 Swift 语言的基本计算器。该计算器能够解析和计算包含 、-、* 和 / 的数学表达式&#xff0c;并且遵循运算符的优先级规则。整数除法仅保留整数部分&#xff0c;不能使用 eval() 这样的内置解析方法。 描述 给你一个字符串表…...

Elasticsearch Java High Level Client [7.17] 使用

es 的 HighLevelClient存在es源代码的引用&#xff0c;结合springboot使用时&#xff0c;会存在es版本的冲突&#xff0c;这里记录下解决冲突和使用方式&#xff08;es已经不建议使用这个了&#xff09;。 注意es服务端的版本需要与client的版本对齐&#xff0c;否则返回数据可…...

[多线程]基于环形队列(RingQueue)的生产者-消费者模型的实现

标题&#xff1a;[多线程]基于环形队列&#xff08;RingQueue&#xff09;的生产者-消费者模型 水墨不写bug 一、模型实现 接下来我们要实现一个基于环形队列&#xff08;RingQueue&#xff09;的生产者-消费者模型。该模型使用信号量和互斥锁来保证生产者和消费者之间的同步与…...

HAL库STM32常用外设—— CAN通信(一)

文章目录 一、CAN是什么&#xff1f;1.1 CAN应用场景1.2 CAN通信优势 二、CAN基础知识介绍2.1 CAN总线结构2.2 CAN总线特点2.2.1 CAN总线的数据传输特点2.2.2 位时序和波特率 2.3 CAN位时序和波特率2.3 CAN物理层2.3.1 CAN 物理层特性2.3.2 CAN 收发器芯片介绍 2.4 CAN协议层2.…...

分页查询的实现

目录 前言 一.问题描述 二.后端实现步骤 2.1配置PageHelper插件 ①导入依赖 ②在application.yml配置文件中添加相关配置 2.2编写一个入门的程序&#xff0c;体验分页过程 2.3定义一个vo&#xff0c;用来收集分页后的所有信息 2.4修改serviceImpl层的代码 2.5动态设…...

Sourcetree——使用.gitignore忽略文件或者文件夹

一、为何需要文件忽略机制&#xff1f; 1.1 为什么要会略&#xff1f; 对于开发者而言&#xff0c;明智地选择忽略某些文件类型&#xff0c;能带来三大核心优势&#xff1a; 仓库纯净性&#xff1a;避免二进制文件、编译产物等污染代码库 安全防护&#xff1a;防止敏感信息&…...

Thinkphp的belongsToMany(多对多) 和 hasManyThrough(远程一对多)的区别是什么?

虽然 belongsToMany&#xff08;多对多&#xff09; 和 hasManyThrough&#xff08;远程一对多&#xff09; 都会使用 JOIN 查询&#xff0c;但它们的核心区别在于 关联关系的本质不同&#xff0c;具体如下&#xff1a; 1️⃣ belongsToMany&#xff08;多对多&#xff09; &a…...

DataWhale 大语言模型 - 大模型技术基础

本课程围绕中国人民大学高瓴人工智能学院赵鑫教授团队出品的《大语言模型》书籍展开&#xff0c;覆盖大语言模型训练与使用的全流程&#xff0c;从预训练到微调与对齐&#xff0c;从使用技术到评测应用&#xff0c;帮助学员全面掌握大语言模型的核心技术。并且&#xff0c;课程…...

Docker+Flask 实战:打造高并发微服务架构

DockerFlask 实战&#xff1a;打造高并发微服务架构 今天我们要深入探讨一个非常热门且实用的主题&#xff1a;基于 Docker 部署 Python Flask 应用。Docker 作为当下最流行的容器化技术&#xff0c;已经广泛应用于各种开发和部署场景&#xff0c;尤其是在微服务架构中。而 Fl…...

前端跨域如何调试,以及相关概念梳理【环境变量 本地代理 正向代理 反向代理 OPTIONS请求 CDN 等】

跨域报错 一 前端日常开发时&#xff0c;项目的部署地址和接口请求的地址一般是同源的&#xff0c;不会跨域。 例如项目的测试环境部署在https://my-dev.BeatingWorldLine.com/xxx, 测试环境的访问接口域名也要相同来保证不跨域https://my-dev.BeatingWorldLine.com/api/xxx, …...

【区块链】以太坊

学习视频源链接&#xff1a; https://www.bilibili.com/video/BV1Vt411X7JF/ 本文是根据肖老师的视频进行的笔记记录 bitcoin 1.0 区块链 以太坊 2.0区块链 以太坊 设置了 memory hard mining puzzle &#xff0c;这造成了asic resistance&#xff0c; 后续 proof of work &a…...

MCU的工作原理:嵌入式系统的控制核心

MCU的工作原理可以概括为以下几个步骤&#xff1a; 1. 初始化 上电后&#xff0c;MCU从Flash存储器中加载程序代码&#xff0c;并初始化外设和寄存器。 2. 任务执行 根据程序逻辑&#xff0c;MCU执行数据处理、外设控制和通信等任务。通过中断系统实时响应外部事件。 3. 低…...

离线服务器ollama新增qwen2:0.5b模型

离线服务器ollama新增qwen2:0.5b模型 Dify集成ollama前面已经介绍过离线服务器CentOS使用的docker安装的ollama&#xff0c;其中在ollama中已经安装了deepseek-r1:1.5b。目前的需求是需要再安装一个qwen2:0.5b的模型&#xff0c;那么如何安装呢&#xff1f; 1.首先在有网的服…...

Ubuntu20.04安装运行DynaSLAM

目录 一、安装Anaconda 二、相关依赖库安装 1、boost安装 2、Eigen 3安装 3、opencv安装 4、Pangolin安装 三、配置Mask_RCNN环境 四、DynaSLAM编译 五、DynaSLAM运行 一、安装Anaconda 打开以下链接&#xff1a; Index of / 下载和自己系统匹配的安装包。这里下…...

Apache Shiro反序列化漏洞深度剖析:从原理到利用

引言 在Web安全的世界里&#xff0c;反序列化漏洞一直是最危险的漏洞类型之一。今天&#xff0c;我们将深入探讨Apache Shiro框架中的两个著名反序列化漏洞.通过通俗易懂的解释和详细的实例&#xff0c;帮助你理解这类漏洞的本质和危害。 Shiro框架与"记住我"功能简…...

Android UI 组件系列(二):Button 进阶用法

引言 在上一篇博客中&#xff0c;我们介绍了 Button 的基本用法和常见属性&#xff0c;掌握了 Button 的基础知识。然而&#xff0c;在实际开发中&#xff0c;Button 远不止于简单的点击功能&#xff0c;它还可以支持不同的变体、丰富的自定义样式&#xff0c;以及更灵活的状态…...

CentOS-7安装Docker(更新时间:2025-03-12)

CentOS-7安装Docker 该文章记录在CentOS 7上安装Docker的过程和步骤&#xff0c;以及在安装过程中遇到的困难和解决方案。 目录 CentOS-7安装Docker一、环境准备二、安装Docker1.验证服务器是否接入互联网2. 检查CentOS内核版本3.使用root权限登录CentOS。确保yum包更新到最新…...

网络空间安全(31)安全巡检

一、定义与目的 定义&#xff1a; 安全巡检是指由专业人员或特定部门负责&#xff0c;对各类设施、设备、环境等进行全面或重点检查&#xff0c;及时发现潜在的安全隐患或问题。 目的&#xff1a; 预防事故发生&#xff1a;通过定期的安全巡检&#xff0c;及时发现并解决潜在的…...

Kubernetes学习笔记-移除Nacos迁移至K8s

项目服务的配置管理和服务注册发现由原先的Nacos全面迁移到Kubernetes上。 一、移除Nacos 移除Nacos组件依赖。 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <…...

Docker 构建 nginx-redis-alpine 项目详解

Docker 构建 nginx-redis-alpine 项目详解 一、课程概述 嘿&#xff0c;朋友们&#xff01;今天咱们要深入探索一个超级实用的项目 ——nginx-redis-alpine&#xff01;这个项目可不简单&#xff0c;它包含了好多重要的知识点&#xff0c;像文件目录结构、核心文件的作用及配…...

【教学类-43-26】20240312 数独4宫格的所有可能(图片版 576套样式,空1格-空8格,每套65534张*576小图=3千万张小图)

背景需求&#xff1a; 之前做了三宫格所有可能图片 510小图*12套6120图&#xff0c;所以3分钟就生成了 【教学类-43-25】20240311 数独3宫格的所有可能&#xff08;图片版 12套样式&#xff0c;空1格-空8格&#xff0c;每套510张&#xff0c;共6120小图&#xff09;-CSDN博客…...

ChromeOS 134 版本更新

ChromeOS 134 版本更新 一、ChromeOS 134 更新内容 1. ChromeOS 自助终端&#xff08;Kiosk&#xff09;模式支持隔离 Web 应用&#xff08;Isolated Web Apps&#xff09; 从 ChromeOS 134 开始&#xff0c;自助终端&#xff08;Kiosk&#xff09;模式支持 隔离 Web 应用&a…...

Redis面试篇

目录 Redis面试篇 1.什么是Redis&#xff1f;作用是什么&#xff1f; 2.什么是缓存穿透、缓存击穿、缓存雪崩 2.1缓存穿透 2.2缓存击穿 2.3缓存雪崩 3.redis如何持久化 1. RDB&#xff08;快照存储&#xff09; 2. AOF&#xff08;追加日志&#xff09; 4.Redis 的过…...

C#中通过Response.Headers设置自定义参数

一、基础设置方法 1. 直接添加自定义头 // ASP.NET Core方案 Response.Headers.Append("X-API-Version", "2.3.1"); Response.Headers.Append("Custom-Auth-Token", Guid.NewGuid().ToString());• 底层原理&#xff1a;通过IHeaderDictionary…...

C++标准模板库学习--函数模板返回值参数类型

template<typename T1, typename T2> 2 T1 max (T1 a, T2 b) 3 { 4 return b < a ? a : b; 5 } 6 ... 7 auto m ::max(4, 7.2); // OK, 不过返回类型与第一个参数类型一样 如何解决模板的返回类型 法一&#xff0c;使用decltype进行类型推断&#xff0c;在编译时…...

BUG修复 | 一次钉钉工作台应用远程调试实战(开发者工具)

#1 ℹ️背景故事 最近用户反馈&#xff0c;钉钉工作台的应用无法正常使用&#xff0c;卡在自动登录页面。 天&#xff0c;这是运行10年的老程序&#x1f631;&#xff0c;我当时真是吓得不轻。这老古董完全不记得怎么改了&#x1f602;。 #2 &#x1f41e;开启远程调试 钉钉…...

[目标检测] 训练之前要做什么

背景&#xff1a;训练一个Yolo8模型&#xff0c;在训练之前&#xff0c;数据集的处理是影响效果的关键因素。 Step1 定义规则 什么是人/车&#xff0c;比如人的话可能是站着的人&#xff0c;如果是骑电动车/自行车就不算是人。 Step2 收集数据集 1. 自己标注。如果是自己标…...

一窥DeepSeek开源EPLB项目:揭开技术背后的面纱

摘要 在DeepSeek开源DualPipe项目的同一天&#xff0c;EPLB项目也正式对外公开。EPLB&#xff08;Enhanced Pipeline Balancing&#xff09;并非一蹴而就的奇迹&#xff0c;而是经过长时间的研发与优化。该项目旨在通过改进管道平衡机制&#xff0c;提升系统的稳定性和效率。本…...

达梦数据库中插入导出图片的方法与应用

达梦数据库中插入导出图片的方法与应用 在数据库的实际应用场景中&#xff0c;图片存储是一项常见且重要的需求。以电商平台为例&#xff0c;商品展示图片是吸引消费者的关键元素&#xff1b;而在社交软件里&#xff0c;用户头像更是个人形象的直观体现。针对达梦数据库&#…...

问deepseek: OpenFOAM并行分区后,是如何实现ldumatrix矩阵向量乘法计算逻辑的?

在OpenFOAM中&#xff0c;lduMatrix 是用于存储稀疏矩阵的类&#xff0c;支持并行计算。并行分区后&#xff0c;lduMatrix 的矩阵向量乘法通过以下步骤实现&#xff1a; 1. 矩阵分区 分区&#xff1a;将矩阵和向量分配到多个处理器上&#xff0c;每个处理器负责一部分。接口&…...

linux(ubuntu)中Conda、CUDA安装Xinference报错ERROR: Failed to build (llama-cpp-python)

文章目录 一、常规办法二、继续三、继续四、缺少 libgomp库&#xff08;最终解决&#xff09;在 Conda 环境中安装 libgomp 如果符合标题情况 执行的&#xff1a; pip install "xinference[all]"大概率是最终解决的情况。 一、常规办法 llama-cpp-python 依赖 CMak…...

蓝耘携手通义万象 2.1 图生视频:开启创意无限的共享新时代

在科技飞速发展的今天&#xff0c;各种新奇的技术不断涌现&#xff0c;改变着我们的生活和工作方式。蓝耘和通义万象 2.1 图生视频就是其中两项非常厉害的技术。蓝耘就像是一个超级大管家&#xff0c;能把各种资源管理得井井有条&#xff1b;而通义万象 2.1 图生视频则像是一个…...

04 1个路由器配置一个子网的dhcp服务

前言 这是最近一个朋友的 ensp 相关的问题, 这里来大致了解一下 ensp, 计算机网络拓扑 相关基础知识 这里一系列文章, 主要是参照了这位博主的 ensp 专栏 这里 我只是做了一个记录, 自己实际操作了一遍, 增强了一些 自己的理解 当然 这里仅仅是一个 简单的示例, 实际场景…...

Android studio运行报错处理

没装HAXM报错&#xff1a; Intel HAXM 7.6.5 下载 下载链接&#xff1a; https://www.filehorse.com/download-intel-haxm/54766/download/#google_vignette 运行时弹窗提示&#xff1a;Device manager The emulator process for AVD Pixel_3a_API_34_extension_level_7_x86_6…...