Java中的锁机制

在Java中,锁机制是多线程编程里保障数据一致性与线程安全的关键技术。

1. 内置锁:synchronized关键字

synchronized是Java的内置锁机制,能够保证在同一时刻,只有一个线程可以执行被其修饰的代码块或方法。

用法示例
public class SynchronizedExample {private int count = 0;// 同步方法public synchronized void increment() {count++;}// 同步代码块public void decrement() {synchronized(this) {count--;}}
}
实现原理

synchronized是基于对象头中的Mark Word来实现的。当一个线程访问同步代码块时,会先查看对象的Mark Word。如果Mark Word显示该对象没有被锁定,那么这个线程就会将Mark Word设置为锁定状态,然后开始执行同步代码块。在这个线程执行同步代码块期间,如果其他线程也想访问这个同步代码块,它们会发现对象的Mark Word已经被设置为锁定状态,于是这些线程就会被阻塞,进入等待队列。

2. 显示锁:Lock接口

Lock接口是Java 5引入的,它提供了比synchronized更灵活、更强大的锁控制能力。

核心方法
  • lock():获取锁,如果锁不可用,则线程会被阻塞。
  • unlock():释放锁,必须在finally块中调用,以确保锁一定会被释放。
  • tryLock():尝试获取锁,如果锁可用,则获取锁并返回true;如果锁不可用,则立即返回false,不会阻塞线程。
用法示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockExample {private final Lock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}
}

3. ReentrantLock(可重入锁)

ReentrantLockLock接口的一个重要实现类,它支持可重入锁的特性。所谓可重入锁,就是指同一个线程可以多次获取同一把锁,而不会出现死锁的情况。

特性
  • 公平性ReentrantLock可以设置为公平锁或非公平锁。公平锁会按照线程请求锁的顺序来分配锁,而非公平锁则不保证这一点,有可能后请求的线程先获得锁。
  • 可重入性:同一个线程可以多次获取同一把锁,每获取一次,锁的计数器就会加1,每释放一次,锁的计数器就会减1,当计数器为0时,锁才会被真正释放。
公平锁示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class FairLockExample {private final Lock fairLock = new ReentrantLock(true); // true表示公平锁public void performTask() {fairLock.lock();try {// 执行任务} finally {fairLock.unlock();}}
}

4. ReentrantReadWriteLock(读写锁)

ReentrantReadWriteLock提供了读写分离的锁机制,它维护了一对锁,一个读锁和一个写锁。

特性
  • 读锁:允许多个线程同时获取读锁,用于并发读取共享资源。
  • 写锁:写锁是排他锁,同一时刻只允许一个线程获取写锁,用于修改共享资源。
  • 读写互斥:读锁和写锁不能同时被获取,即当有线程获取了读锁时,其他线程不能获取写锁;当有线程获取了写锁时,其他线程不能获取读锁和写锁。
用法示例
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class Cache {private final ReadWriteLock rwLock = new ReentrantReadWriteLock();private Object data;public Object read() {rwLock.readLock().lock();try {return data;} finally {rwLock.readLock().unlock();}}public void write(Object newData) {rwLock.writeLock().lock();try {data = newData;} finally {rwLock.writeLock().unlock();}}
}

5. StampedLock(邮戳锁)

StampedLock是Java 8引入的一种新的锁机制,它提供了比ReentrantReadWriteLock更细粒度的锁控制。可以有效应对A-B-A问题。

特性
  • 乐观读锁:乐观读锁是一种无锁机制,它允许在没有获取锁的情况下读取共享资源。读取完成后,需要验证资源是否在读取期间被修改过,如果没有被修改过,则读取有效;如果被修改过,则需要重新读取。
  • 悲观读锁和写锁:与ReentrantReadWriteLock的读锁和写锁类似,但StampedLock的悲观读锁和写锁是通过返回一个邮戳(stamp)来控制的。
用法示例
import java.util.concurrent.locks.StampedLock;public class Point {private double x, y;private final StampedLock sl = new StampedLock();public double distanceFromOrigin() {long stamp = sl.tryOptimisticRead(); // 尝试乐观读double currentX = x, currentY = y;if (!sl.validate(stamp)) { // 验证是否有写操作发生stamp = sl.readLock(); // 获取悲观读锁try {currentX = x;currentY = y;} finally {sl.unlockRead(stamp);}}return Math.sqrt(currentX * currentX + currentY * currentY);}
}

6. 锁的优化

锁粗化(Lock Coarsening)

锁粗化是指将多次连续的加锁和解锁操作合并为一次加锁和解锁操作,以减少锁的获取和释放带来的性能开销。

锁消除(Lock Elimination)

锁消除是指在编译时,Java编译器会对一些代码进行分析,如果发现某些锁是不必要的,就会将这些锁消除掉,从而提高代码的执行效率。

偏向锁(Biased Locking)

偏向锁是一种针对单线程环境的锁优化机制。当一个线程第一次获取锁时,锁会被标记为偏向锁,并记录该线程的ID。当该线程再次获取锁时,无需进行任何同步操作,直接获取锁,从而提高了单线程环境下的性能。

轻量级锁(Lightweight Locking)

轻量级锁是一种在多线程环境下,但线程竞争不激烈时的锁优化机制。当线程竞争不激烈时,轻量级锁可以避免线程的阻塞和唤醒操作,从而提高了性能。

自旋锁(Spin Lock)

自旋锁是指当一个线程获取锁失败时,它不会立即被阻塞,而是会在原地循环等待,直到锁被释放。自旋锁适用于锁的持有时间较短的场景,可以减少线程的阻塞和唤醒带来的性能开销。

7. 选择合适的锁

  • synchronized:适用于简单的同步场景,代码简洁,由JVM自动管理锁的获取和释放。
  • ReentrantLock:适用于需要更灵活的锁控制的场景,如可中断锁、公平锁等。
  • ReentrantReadWriteLock:适用于读多写少的场景,可以提高并发读取的性能。
  • StampedLock:适用于读多写少且读操作性能要求较高的场景,提供了乐观读锁机制,进一步提高了读操作的性能。

通过合理使用这些锁机制,你可以在多线程编程中实现高效且安全的并发控制。

重入锁和内置锁的选用

在Java里,重入锁(ReentrantLock)和内置锁(synchronized)都可用于实现线程同步,不过它们的适用场景存在差异。下面为你介绍选择使用重入锁还是内置锁的依据:

优先考虑内置锁的情况

  • 语法简洁:内置锁是通过synchronized关键字来实现的,无需手动释放锁,JVM会自动处理锁的获取和释放,这样能降低因忘记释放锁而导致死锁的风险。
    public synchronized void method() {// 同步代码块
    }
    
  • 对性能要求不高:在JDK 1.6之后,Java对synchronized进行了一系列优化,像偏向锁、轻量级锁等,使得它的性能和ReentrantLock相差不大。
  • 锁的使用场景简单:若只是需要对方法或代码块进行简单的同步,内置锁完全可以满足需求。

优先考虑重入锁的情况

  • 需要公平锁:重入锁可以通过构造函数指定使用公平锁(new ReentrantLock(true)),公平锁会按照线程请求锁的顺序来分配锁,能避免某些线程长时间等待锁的情况。而内置锁只能是非公平锁。
  • 需要灵活的锁控制:重入锁提供了一些高级功能,如可中断锁、尝试锁(tryLock())、带超时的锁获取等。
    Lock lock = new ReentrantLock();
    try {// 尝试获取锁,若锁被其他线程持有,则当前线程可被中断lock.lockInterruptibly();// 执行同步操作
    } catch (InterruptedException e) {Thread.currentThread().interrupt();
    } finally {lock.unlock();
    }
    
  • 需要实现条件变量(Condition):重入锁可以和Condition接口配合使用,实现更灵活的线程等待和唤醒机制,比如实现生产者 - 消费者模式。
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();// 等待条件
    lock.lock();
    try {while (!conditionMet()) {condition.await();}
    } finally {lock.unlock();
    }// 唤醒等待的线程
    lock.lock();
    try {condition.signalAll();
    } finally {lock.unlock();
    }
    

性能方面的考量

  • 低竞争场景:在竞争不激烈的情况下,内置锁和重入锁的性能差距不大。
  • 高竞争场景:如果线程之间对锁的竞争非常激烈,重入锁的性能可能会略优于内置锁,因为重入锁提供了更多的锁优化选项,例如使用非公平锁可以减少线程的上下文切换。

总结

  • 推荐优先使用内置锁:因为它的语法简洁,由JVM自动管理锁的获取和释放,降低了出错的概率。
  • 在需要高级特性时使用重入锁:例如公平锁、可中断锁、条件变量等。

示例对比

内置锁示例
public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}
}
重入锁示例
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final ReentrantLock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}
}

通过对比可以看出,内置锁的代码更加简洁,而重入锁则提供了更灵活的控制方式。你可以根据具体的需求来选择合适的锁机制。

理解各种锁的特性和适用场景

Java锁机制中的各类锁是根据不同场景和需求设计的,理解它们的核心概念、实现方式及适用场景对编写高效、线程安全的代码至关重要。以下从分类维度详细解析常见的锁类型:

一、按锁的特性分类

1. 乐观锁(Optimistic Locking)
  • 核心思想:假设数据不会被其他线程修改,因此不阻塞访问,仅在更新时检查是否有冲突。
  • 实现方式
    • 版本号机制:数据记录中增加version字段,更新时比较版本号是否一致(如数据库的UPDATE ... WHERE version = ?);
    • CAS(Compare-And-Swap):Java中通过Unsafe类或Atomic包实现(如AtomicIntegergetAndIncrement())。
  • 使用场景:读多写少、冲突概率低的场景(如缓存更新、统计计数)。
  • 示例
    // AtomicInteger基于CAS实现乐观锁
    private AtomicInteger counter = new AtomicInteger(0);
    public void increment() {counter.getAndIncrement(); // 内部使用CAS,无需显式加锁
    }
    
2. 悲观锁(Pessimistic Locking)
  • 核心思想:假设数据随时会被修改,因此访问时直接加锁,阻塞其他线程。
  • 实现方式
    • synchronized关键字:Java内置的对象锁(如synchronized(this));
    • ReentrantLock:可重入锁,功能更灵活(如支持公平锁、可中断锁)。
  • 使用场景:写多、冲突概率高的场景(如库存扣减、银行转账)。
  • 示例
    // synchronized实现悲观锁
    public synchronized void transferMoney() {// 操作共享资源
    }
    
3. 自旋锁(Spin Lock)
  • 核心思想:当锁被占用时,线程不挂起(不放弃CPU时间片),而是循环尝试获取锁,直到成功。
  • 实现方式
    • 基于CAS实现(如AtomicBooleancompareAndSet());
    • JVM内部的轻量级锁(偏向锁膨胀后可能升级为自旋锁)。
  • 使用场景:锁持有时间短、线程不希望频繁挂起/唤醒的场景(如JDK内部的ConcurrentHashMap)。
  • 优点:避免线程上下文切换,提升性能;
  • 缺点:若锁长时间被占用,会浪费CPU资源。
  • 示例
    // 自定义简单自旋锁
    public class SpinLock {private AtomicBoolean locked = new AtomicBoolean(false);public void lock() {while (!locked.compareAndSet(false, true)) {// 循环等待,自旋}}public void unlock() {locked.set(false);}
    }
    

二、按JVM实现分类

4. 偏向锁(Biased Locking)
  • 核心思想:在单线程环境下,锁偏向第一个获取它的线程,后续该线程无需再进行同步操作。
  • 实现方式
    • 对象头中的Mark Word存储偏向线程ID;
    • 当有其他线程尝试竞争锁时,偏向锁会被撤销并升级为轻量级锁。
  • 使用场景:只有一个线程访问同步块的场景(如单例模式的双重检查锁)。
  • 优点:无竞争时几乎无开销,提升单线程性能。
5. 轻量级锁(Lightweight Lock)
  • 核心思想:多线程交替执行同步块时,通过CAS避免重量级锁的线程挂起/唤醒操作。
  • 实现方式
    • 线程进入同步块时,JVM在栈帧中创建锁记录(Lock Record);
    • 通过CAS将对象头的Mark Word指向锁记录,成功则获取锁,失败则升级为重量级锁。
  • 使用场景:线程竞争不激烈,锁持有时间短的场景。
6. 重量级锁(Heavyweight Lock)
  • 核心思想:依赖操作系统的互斥量(Mutex)实现,线程竞争锁失败时会被挂起(进入内核态)。
  • 实现方式
    • 基于操作系统的pthread_mutex_t(Linux)或CRITICAL_SECTION(Windows);
    • 对象头的Mark Word指向重量级锁的指针,锁竞争时涉及用户态与内核态的切换。
  • 使用场景:线程竞争激烈,锁持有时间长的场景。
  • 缺点:性能开销大,因为涉及内核态与用户态的切换。

三、按API实现分类

7. 可重入锁(Reentrant Lock)
  • 核心思想:允许同一个线程多次获取同一把锁,而不会被阻塞(通过计数器实现)。
  • 实现方式
    • synchronized关键字(隐式可重入);
    • ReentrantLock(显式可重入,需调用lock()unlock())。
  • 使用场景:方法嵌套调用同步块的场景(如递归函数)。
  • 示例
    // ReentrantLock实现可重入锁
    private ReentrantLock lock = new ReentrantLock();
    public void outerMethod() {lock.lock();try {innerMethod();} finally {lock.unlock();}
    }public void innerMethod() {lock.lock(); // 同一线程可再次获取锁try {// 操作共享资源} finally {lock.unlock();}
    }
    
8. 公平锁(Fair Lock)
  • 核心思想:锁的获取顺序按请求时间排序,先到先得(避免线程“饥饿”)。
  • 实现方式
    • ReentrantLock(true):构造函数传入true启用公平锁;
    • 基于FIFO队列实现,线程竞争锁时会进入队列等待。
  • 使用场景:对线程执行顺序敏感的场景(如资源分配)。
  • 缺点:公平锁的性能通常低于非公平锁,因为需要维护队列。
9. 读写锁(ReadWrite Lock)
  • 核心思想:将锁分为读锁和写锁,允许多个线程同时读,但写时互斥。
  • 实现方式
    • ReentrantReadWriteLock:支持读锁(共享锁)和写锁(排他锁);
    • 读锁可被多个线程同时持有,写锁只能被一个线程持有,且写锁存在时禁止读锁。
  • 使用场景:读多写少的场景(如缓存更新、配置读取)。
  • 示例
    // 读写锁示例
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private Lock readLock = rwLock.readLock();
    private Lock writeLock = rwLock.writeLock();public void readData() {readLock.lock();try {// 读取共享数据} finally {readLock.unlock();}
    }public void writeData() {writeLock.lock();try {// 修改共享数据} finally {writeLock.unlock();}
    }
    
10. 分段锁(Striped Lock)
  • 核心思想:将锁分段管理,不同段的锁相互独立,减少锁竞争。
  • 实现方式
    • ConcurrentHashMap(JDK 7及以前):内部使用分段数组(Segment),每个Segment独立加锁;
    • JDK 8后改用CAS+Synchronized实现,但分段锁思想仍在其他场景中使用。
  • 使用场景:大规模数据并发操作的场景(如分布式缓存)。

四、按锁的特性扩展

11. 可中断锁(Interruptible Lock)
  • 核心思想:线程在等待锁的过程中可被其他线程中断。
  • 实现方式
    • ReentrantLocklockInterruptibly()方法;
    • synchronized不支持可中断,只能通过Thread.interrupt()标记中断状态。
  • 使用场景:需要取消长时间等待的场景(如超时控制)。
  • 示例
    ReentrantLock lock = new ReentrantLock();
    try {lock.lockInterruptibly(); // 可中断的锁获取try {// 操作共享资源} finally {lock.unlock();}
    } catch (InterruptedException e) {Thread.currentThread().interrupt(); // 恢复中断状态
    }
    
12. 锁降级(Lock Degradation)
  • 核心思想:将写锁降级为读锁,保持数据可见性。
  • 实现方式
    • 先获取写锁,修改数据后获取读锁,再释放写锁。
  • 使用场景:写操作后需要保证后续读操作可见性的场景(如缓存更新)。
  • 示例
    ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    Lock writeLock = rwLock.writeLock();
    Lock readLock = rwLock.readLock();public void updateAndRead() {writeLock.lock();try {// 修改数据readLock.lock(); // 获取读锁} finally {writeLock.unlock(); // 释放写锁,保留读锁(锁降级)}try {// 读取数据(确保数据可见性)} finally {readLock.unlock();}
    }
    

五、锁的选择策略

  1. 优先考虑无锁方案:如使用Atomic类、ConcurrentHashMap等无锁数据结构。
  2. 读多写少→乐观锁/CAS:冲突概率低时性能最优。
  3. 写多冲突高→悲观锁:如synchronizedReentrantLock
  4. 锁持有时间短→自旋锁:避免线程上下文切换。
  5. 公平性需求→公平锁:但需牺牲一定性能。
  6. 读写分离→读写锁:如ReentrantReadWriteLock

理解各种锁的特性和适用场景,结合具体业务需求选择合适的锁,是编写高效并发代码的关键。

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

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

相关文章

YOLOv11: AN OVERVIEW OF THE KEY ARCHITECTURAL ENHANCEMENTS目标检测论文精读(逐段解析)

YOLOv11: AN OVERVIEW OF THE KEY ARCHITECTURAL ENHANCEMENTS目标检测论文精读(逐段解析) 论文地址:https://www.arxiv.org/abs/2410.17725 Rahima Khanam and Muhammad Hussain Ultralytics公司发布 CVPR 2024 论文写的比较简单&#xff…

【Erdas实验教程】025:遥感图像辐射增强(雾霾去除)

文章目录 一、雾霾去除原理二、雾霾去除案例一、雾霾去除原理 遥感影像雾霾去除的核心原理是消除大气散射对电磁波的干扰,恢复地物真实反射信息。Haze Reduction 工具的原理: 该工具基于暗目标法(Dark Object Subtraction, DOS),适用于去除因大气散射(雾霾本质是大气颗…

Language Models are Unsupervised Multitask Learners :语言模型是无监督的多任务学习者

摘要 自然语言处理任务,如问答、机器翻译、阅读理解和摘要,通常通过在特定任务的数据集上进行监督学习来解决。我们展示了语言模型在训练于一个包含数百万网页的新数据集——WebText——时,可以无需任何显式监督就开始学习这些任务。当模型以…

SQL语句全攻略:从基础到进阶的编程之旅

目录 一、引言二、SQL 基础语法2.1 SQL 语句写法顺序2.2 关联查询2.3 数据处理常用函数和运算符 三、数据库和表的基本操作3.1 创建数据库3.2 使用数据库3.3 创建表 四、基础增删改查操作4.1 插入数据(增)4.2 查询数据(查)4.3 更新…

Kafka的下载安装

目录 一、前期准备 1、查看网卡: 2、配置静态IP 3、设置主机名 4、配置IP与主机名映射 5、关闭防火墙 6、配置免密登录 二、JDK的安装 三、Zookeeper的安装 四、Kafka的安装 1、Kafka的下载安装 2、修改配置文件 4、分发文件 5、修改其他节点broker.i…

opencv入门(6) TrackBar调整图片和键盘响应

文章目录 1 创建trackbar2 使用userdata传入函数3 键盘响应 1 创建trackbar 1.trackbar名称 2.创建在哪个窗口上 3.拖动trackbar改变的值 4.trackBar的最大值 5.trackbar改变时的回调函数 6. 带入回调函数的数据,可以不用带,是一个void指针 createTrackbar(“Value …

QT<33> 修改窗口标题栏背景颜色

前言: 在做项目或者开发QT软件时,如果想要修改窗口标题栏背景颜色,发现没有代码可以直接设置,目前有两种方法可以设置。 第一种,自定义一个界面类,用QLabelQWidget实现,QLabel当作标题栏。 第…

JavaEE-博客系统项目

项目介绍 准备工作 创建数据表 创建项目 添加依赖 创建对应目录 除了基本的数据层mapper,业务层service,交互层controller,还创建了公共类的层common,枚举类层enums,异常类层,和实体类层pojo。 配置项目配…

Java项目:基于SSM框架实现的软件工程项目管理系统【ssm+B/S架构+源码+数据库+毕业论文+开题报告】

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本项目管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息&am…

[按键手机安卓/IOS脚本插件开发] 按键插件调试与判断循环结构辅助工具

实现按键插件的核心原理 通过一个table类型的QMPlugin变量实现按键精灵调用Lua函数,例如 -- Lua代码 -- 实现两数相加求和 function QMPlugin.Add(a, b) return a b end 将以上代码保存成.lua文件,例如test.lua后,放入按键精灵手机助手的p…

提示词框架(9)--CARE

提示词框架不止是AI的框架,也可以是我们的思考框架,拆解问题的方法!!! CARE框架是一种用于优化提示词设计的方法,它帮助用户更有效地与AI进行交互,特别是在需要获取特定信息或实现某些任务时。…

uniapp+vue2 input不显示明文密码,点击小眼睛显示或隐藏密码

<u-input placeholder"请输入密码" prefixIcon"lock" :password"showPassword" v-model"formData.password"prefixIconStyle"font-size: 25px;color: #3C9CFF" border"none"><template slot"suffix…

时间序列的类增量学习:基准与评估

论文地址&#xff1a;https://dl.acm.org/doi/abs/10.1145/3637528.3671581 论文源码&#xff1a;https://github.com/zqiao11/TSCIL 会议&#xff1a;SIGKDD 2024&#xff08;CCF-A&#xff09; 现实环境本质上是非平稳的&#xff0c;随着时间推移常会引入新类别。这在时间序…

智能攻击原理和架构

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 智能攻击系统基于**人工智能&#xff08;AI&#xff09;与大语言模型&#xff08;LLM&#xff09;技术**&#xff0c;通过**环境感知→自主决策→动态执行→对抗进化**的闭环架构实现高效网络入侵。以下…

Beamer-LaTeX学习(教程批注版)【4】

众所周知&#xff0c;随着显示器普及宽屏化&#xff0c;PPT排版需要多列化来充分利用页面的横向空间。 这节课我们来学习如何创建列&#xff0c;即在beamer中增加列内容&#xff0c;以及如何对齐列。 1 使用不同的宽度来创建列 在beamer中我们通过在页面上使用columns环境来创…

通俗理解JVM细节-面试篇

文章目录 前言JVM概述JVM是什么&#xff1f;解决了什么问题&#xff1f;JVM运行流程JVM 与 JRE&#xff0c;JDK的关系 JVM内存结构JVM区域划分程序计数器栈堆方法区 类加载机制五个阶段加载验证准备解析初始化总结双亲委派模型 垃圾回收内存管理什么是GC&#xff1f;如何判定谁…

意识边疆保卫战:22:47深圳AI-BioFab人机融合危机全息实录

前言 前些天发现了一个巨牛的人工智能免费学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 &#x1f9e0;《意识边疆保卫战&#xff1a;22&#xff1a;47深圳AI-BioFab人机融合危机全息实录》 副标题&#xff1a;机械义…

langchain从入门到精通(三十四)——RAG优化策略(十)父文档检索器实现拆分和存储平衡

1. 拆分文档与检索的冲突 在 RAG 应用开发中&#xff0c;文档拆分 和 文档检索 通常存在相互冲突的愿望&#xff0c;例如&#xff1a; 我们可能希望拥有小型文档&#xff0c;以便它们的嵌入可以最准确地反映它们的含义&#xff0c;如果太长&#xff0c;嵌入/向量没法记录太多…

Javaweb - 7 xml

XML 是EXtensible Markup Language 的缩写&#xff0c;翻译过来就是可扩展标记语言。即&#xff0c;XML 和 HTML 一样&#xff0c;都是标记语言&#xff0c;也就是说&#xff0c;它们的基本语法都是标签。 特点&#xff1a; 1. 可扩展&#xff1a;即 XML 是允许自定义格式的。但…

qml实现 裁剪进度条

我们需要实现一个垂直进度条效果&#xff0c;但使用图片裁剪的方式。具体需求&#xff1a; 1. 图片位置固定&#xff0c;容器对齐其左边和下边&#xff0c;宽度和图片相同。 2. 容器背景透明&#xff0c;调整裁剪容器的高度&#xff0c;使其有高度进度条的感觉&#xff08;从下…