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

JAVA 锁机制【待完善】

锁是作用于在多线程环境中实现对共享资源并发访问控制的机制,确保同一时间只有一个线程能够访问特定资源,从而保障了数据的一致性和完整性。

作用:防止多个线程同时修改共享数据导致的不一致问题,保证线程安全。

java 的锁分为两类:

  1. 第一类是 synchronized 同步关键字,这个关键字属于隐式的锁,是 jvm 层面实现,使用的时候看不见;
  2. 第二类是 Lock 接口以及对应的各种实现类,这属于显式的锁,就是我们能在代码层面看到锁这个对象,而这些个对象的方法实现,大都是直接依赖 CPU 指令的,无关 jvm 的实现。
    在这里插入图片描述

锁分类

名称 说明 场景
独占锁 / 互斥锁(Mutex) 确保同一时间只有一个线程能够持有,共享资源(阻塞) ReentrantLock、Sychronized、ReentrantReadWriteLock的写锁
共享锁 锁可被多个线程所持有 ReentrantReadWriteLock的读锁
读写锁(Read-Write Lock) 允许多个线程同时读取共享资源,但在写入时只能有一个线程进行操作(阻塞), 基于互斥锁和条件变量,维护读写计数器。 适合读多写少的场景 ReentrantReadWriteLock、StampedLock
偏向锁 锁会偏向于第一个获得它的线程,当这个线程再次请求锁的时候不需要进行任何同步操作,减少锁操作的开销。
研究发现,在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁的代价而引入偏向锁。那么显然,一旦另一个线程尝试获得这个锁,那么偏向模式就会结束。另一方面,如果程序的大多数锁都是多个线程访问,那么偏向锁就是多余的。
ReentrantLock
轻量级锁 当偏向锁的条件不满足,但的确有多线程并发争抢同一锁对象时,但并发数不大时,优先使用轻量级锁。 ReentrantLock
自旋锁(Spin Lock) 自旋锁是一个过渡锁,是从轻量级锁升级到重量级锁的过渡。也就是CAS。
适用于锁持有时间较短的场景,避免线程上下文切换的开销。但循环可能导致 CPU 资源浪费
atomic原子类
重入锁 同一个线程可以多次获取同一把锁而不会导致死锁的锁。
特点:需要手动释放锁,适用于需要更灵活控制的场景
ReentrantLock
公平锁 按照线程在队列(FIFO) 中的排队顺序,先到者先拿到锁,避免饥饿现象。 ReentrantLock(构造函数中指定公平策略)
非公平锁 当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的 ReentrantLock(默认非公平)、synchronized
无锁编程 通过原子操作(如 CAS)和数据结构(如队列、栈)避免使用传统锁机制 Atomic原子操作类
分布式锁 在分布式系统中基于 ZooKeeper、基于 Redis 协调多个节点对共享资源的访问.
实现复杂且需要考虑网络延迟、节点故障等问题。

乐观锁和悲观锁

按照锁的特性可分为分乐观锁和悲观锁两类,乐观锁(Optimistic Locking)和悲观锁(Pessimistic Locking)是两种不同的并发控制策略,用于解决多线程环境下的数据一致性问题。

特性 乐观锁 悲观锁
核心思想 假设冲突不会发生 假设冲突一定会发生
锁机制 无锁,基于版本号或 CAS 加锁,在操作数据之前,先获取锁,确保独占访问。基于锁的同步机制
优缺点 高并发读性能,避免了锁竞争
实现复杂,高冲突时重试多
简单易实现,在高冲突时能避免数据不一致
锁的开销大,可能导致线程阻塞
适用场景 读多写少,低冲突 写频繁,高冲突

它们的核心区别在于对并发冲突的预期和处理方式。

悲观锁: 假设冲突一定会发生,因此在操作数据时会先加锁,确保在操作期间其他线程无法访问或修改该数据。

  • 基于锁的同步机制:synchronized(内置锁) 、ReentrantLock(显式锁)
  • 数据库中的悲观锁:
    • 使用 SELECT … FOR UPDATE 语句锁定
    • 使用事务锁(如 BEGIN TRANSACTION 和 COMMIT)。

乐观锁:假设冲突不会发生,因此在操作数据时不加锁,而是通过某种机制在提交时检查是否发生了冲突,如果冲突则重试,直到成功。

  • 基于版本号(Version Number)/ 时间戳:
    • 每次更新数据时,增加版本号。
    • 提交时检查版本号是否发生变化,若变化则重试。
  • 基于 CAS(Compare-And-Swap):
    • 使用原子操作(如 AtomicInteger 或 Unsafe)来更新数据。
    • 提交时通过 CAS 操作检查并更新数据。
    • 其他乐观锁机制
      Vector 和 CopyOnWriteArrayList:在某些情况下,可以通过这些类实现乐观锁。
      ConcurrentHashMap:在某些操作中,可以通过 putIfAbsent 等方法实现乐观锁。

公平锁与非公平锁

按照锁的顺序分类: 公平锁与非公平锁

特性 公平锁 非公平锁
锁获取顺序 按照请求顺序(FIFO)获取锁 直接尝试抢占锁,忽略队列顺序
优缺点 优点是保证先到先得,避免饥饿;
缺点是上下文切换频繁,吞吐量较低,因为线程频繁进入/退出等待状态。
优点是减少线程切换,线程直接尝试获取锁,吞吐量高;
缺点是可能导致线程饥饿。
适用场景 按顺序访问的场景 对吞吐量要求高,顺序要求不高的场景
实现复杂度 较高,需要维护队列顺序 较低,直接尝试获取锁

构造函数中指定公平策略,默认不公平锁。ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
synchronized 也是不公平锁。

特性 synchronized ReentrantLock ReentrantReadWriteLock StampedLock
锁类型 内置锁、独占锁、可重入锁 可重入锁、独占锁、支持公平锁 独占写锁,共享读锁、支持公平锁、可重入锁 独占写锁,共享读锁(悲观读锁和乐观读锁)
支持中断 不支持 支持(lockInterruptibly) 支持 不支持
尝试锁 支持(tryLock)
锁的释放 自动释放锁 手动释放锁 手动释放锁 手动释放锁
支持条件变量 支持(wait() / notify())但功能较弱 支持(Condition) 支持(Condition) 不支持
适用场景 简单同步需求、低竞争场景 高竞争场景、复杂锁管理需求 读多写少, 高并发读取 读多写少, 引入了乐观读机制

Synchronized 关键字

synchronized 是一个Java 的关键字(内置的同步机制),依赖于 JVM 实现,支持独占锁可重入锁。当对一个类或对象加锁时,线程如果要访问该类或对象必须先获得它的锁。1

  • 可见性: 对一个类或对象加锁时,一个线程如果要访问该类或对象必须先获得它的锁,而这个锁的状态对于其他任何线程都是可见的。并且在释放锁之前会将对变量的修改刷新到共享内存当中,保证资源变量的可见性。
  • 原子性: 被修饰的类或对象的所有操作都是原子的,因为在执行操作之前必须先获得类或对象的,直到执行完才能释放。
  • 有序性: 一个变量在同一个时刻只允许一条线程对其进行 lock 操作,也就确定了线程执行同步代码块的有序性。

synchronized 和 volatile 的区别?
synchronized 和 Lock 区别?
synchronized 和 ReentrantLock 的区别?

在这里插入图片描述
✤ static 表明这是该类的一个静态资源,不管 new 了多少个对象,只有一份
✤ 静态成员不属于任何一个实例对象,是类成员。所以,如果线程 A、B分别 调用同一个实例对象的非静态 synchronized 方法 和 实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态方法时占用是当前类的锁,而访问非静态方法占用是当前实例对象锁
✤ 尽量不要使用 synchronized(String a) 因为JVM中,字符串常量池具有缓存功能!

synchronized可重入的原理:
底层原理维护一个计数器,当线程获取该锁时,计数器加一,再次获得该锁时继续加一,释放锁时,计数器减一,当计数器值为0时,表明该锁未被任何线程所持有,其它线程可以竞争获取锁。

多线程中 synchronized 锁升级:

偏向锁->自旋锁->轻量级锁->重量级锁。锁升级是为了减低了锁带来的性能消耗,按照这个顺序,锁的重量依次增加。

多线程中 synchronized 锁升级的原理:

  1. 在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,
  2. 再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,
  3. 如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,
  4. 执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。
// 双重校验锁实现对象单例(线程安全)
public class Singleton {// 采用 volatile 关键字修饰(禁止JVM的指令重排), 避免在多线程下 初始化顺序混乱private volatile static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {//先判断对象是否已经实例过,没有实例化过才进入加锁代码if (uniqueInstance == null) {//类对象加锁synchronized (Singleton.class) {if (uniqueInstance == null) {// uniqueInstance = new Singleton(); 这段代码其实是分为三步执行// 1.为 uniqueInstance 分配内存空间// 2.初始化 uniqueInstance// 3.将 uniqueInstance 指向分配的内存地址uniqueInstance = new Singleton();}}}return uniqueInstance;}
}

Lock 接口

在这里插入图片描述
在这里插入图片描述

AQS

ReentrantLock

是 Lock 接口的实现类,支持 可重入锁独占锁公平

http://www.dtcms.com/a/524653.html

相关文章:

  • 不平均的分治——根号分治
  • USP-(DeepSpeed-Ulysses-Attention and Ring-Attention)
  • Ubuntu部署集群环境(3台)
  • VoCo-LLaMA: Towards Vision Compression with Large Language Models 译读笔记
  • 国网北京电力建设研究院网站惠州网站建设电话
  • 鹤壁市住房和城乡建设局网站上线了建站教程
  • centos8.5运行ai00-server报错`GLIBC_2.39‘ not found,解决方法
  • 冷换仓的隐性代价:从安全策略到地址信誉体系的重新思考
  • 如何用Vue CLI 创建 Vue 项目
  • 网站开发专业前景完整php网站开发
  • 企业建网站110平米三室一厅简装图片
  • CAS汽车固件签名:从“完成签名”到“安全治理”的演进之路
  • 免费手机网站模板sem竞价推广怎么做
  • 企业数字化转型的关键一步:打通研发全流程
  • Unity 资源导出的问题,依赖关系过多。
  • 网站开发公司内部数据字典深圳网站建设设计公司
  • 网站建设维护公司地址如何做好网站内容
  • STM32_bug总结-运行函数在SystemInit之后就卡死
  • 基于高光谱成像技术的烟叶含水率检测研究进展
  • 聊城网站推广网站可以换域名吗
  • 一个网站 两个域名淄博公司网站建设价格
  • VBA数据结构终极对决:性能实测与行业应用指南
  • 解码Linux文件IO之JPEG图像原理与应用
  • “短小精悍”的边缘AI算力利器:超微SYS-E403-14B-FRN2T服务器评测
  • Gradio全解14——使用Gradio构建MCP的服务器与客户端(4)——Python包命令:uv与uvx实战
  • php是网站开发的语言吗怎么做好市场宣传和推广
  • 做cms网站步骤广东手机网站建设哪家好
  • GreatSQL 配置 SSL 访问:单机与 MGR 集群指南
  • 网站开发进度把握网站备案需要拍照
  • LC104 二叉树的最大深度