偏向锁的基本原理

偏向锁的基本原理

前面说过,大部分情况下,锁不仅仅不存在多线程竞争,而是总是由同一个线程多次获得,为了让线程获取锁的代价更低就引入了偏向锁的概念。怎么理解偏向锁呢?当一个线程访问加了同步锁的代码块时,会在对象头中存储当前线程的 ID后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁。而是直接比较对象头里面是否存储了指向当前线程的偏向锁。如果相等表示偏向锁是偏向于当前线程的,就不需要再尝试获得锁了

为什么要引入偏向锁概念呢

它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。

说白了,就是怕使用者乱用,比方说一个线程的情况下也去竞争锁,避免浪费资源。

偏向锁的获取和撤销逻辑
  1. 首先获取锁 对象的 Markword,判断是否处于可偏向状态。(biased_lock=1、且 ThreadId 为空)
  2. 如果是可偏向状态,则通过 CAS 操作,把当前线程的 ID 写入到 MarkWord
    a) 如果 cas 成功,那么 markword 就会变成这样。 表示已经获得了锁对象的偏向锁,接着执行同步代码块
    b) 如果 cas 失败,说明有其他线程已经获得了偏向锁,这种情况说明当前锁存在竞争,需要撤销已获得偏向锁的线程,并且把它持有的锁升级为轻量级锁(这个操作需要等到全局安全点,也就是没有线程在执行字节码)才能执行
  3. 如果是已偏向状态,需要检查 markword 中存储的
    ThreadID 是否等于当前线程的 ThreadID
    a) 如果相等,不需要再次获得锁,可直接执行同步代码块
    b) 如果不相等,说明当前锁偏向于其他线程,需要撤销偏向锁并升级到轻量级锁

偏向锁的撤销

偏向锁的撤销并不是把对象恢复到无锁可偏向状态(因为偏向锁并不存在锁释放的概念),而是在获取偏向锁的过程中,发现 cas 失败也就是存在线程竞争时,直接把被偏向
的锁对象升级到被加了轻量级锁的状态。对原持有偏向锁的线程进行撤销时,原获得偏向锁的线程
有两种情况:

  1. 原获得偏向锁的线程如果已经退出了临界区,也就是同步代码块执行完了,那么这个时候会把对象头设置成无 锁状态并且争抢锁的线程可以基于 CAS 重新偏向当前线程
  2. 如果原获得偏向锁的线程的同步代码块还没执行完,处于临界区之内,这个时候会把原获得偏向锁的线程升级为轻量级锁后继续执行同步代码块
    在我们的应用开发中,绝大部分情况下一定会存在 2 个以上的线程竞争,那么如果开启偏向锁,反而会提升获取锁的资源消耗。所以可以通过 jvm 参数UseBiasedLocking 来设置开启或关闭偏向锁

流程图分析轻量级锁的基本原理

在这里插入图片描述

轻量级锁的加锁和解锁逻辑

锁升级为轻量级锁之后,对象的 Markword 也会进行相应的的变化。升级为轻量级锁的过程:

  1. 线程在自己的栈桢中创建锁记录 LockRecord。
  2. 将锁对象的对象头中的MarkWord复制到线程的刚刚创
    建的锁记录中。
  3. 将锁记录中的 Owner 指针指向锁对象。
  4. 将锁对象的对象头的MarkWord替换为指向锁记录的指
    针。

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

<think>嗯,用户问的是资源的原理,我得先理解清楚资源在计算机系统中的作用。资源主要用于多线程或多进程环境下,控制对共享资源的访问,防止竞态条件。那它的原理应该涉及到同步机制,比如互斥、信号量这些。 首先,得解释资源的基本概念,为什么要用它。然后可能得分类讨论不同类型的,比如互斥、读写、自旋,各自的特点和适用场景。用户可能需要了解底层实现,比如原子操作、操作系统提供的系统调用,或者硬件支持,比如CAS指令。 还要注意资源可能带来的问题,比如死、性能开销。这部分用户可能没提到,但作为深入介绍应该包含。需要确保回答结构清晰,分步骤或分点说明,符合用户可见层的要求。 另外,用户可能对的实现机制感兴趣,比如忙等待和阻塞的区别,自旋在用户态和内核态的应用。可能需要提到临界区的保护,以及如何保证原子性,比如通过禁止中断、内存屏障等技术。 得检查是否有遗漏的关键点,比如的获取和释放过程,以及在不同编程语言中的实现例子。不过用户的问题比较偏向原理,可能需要保持抽象,不涉及具体代码。还要确保使用正确的中文术语,避免翻译错误。 最后,总结资源的重要性,以及正确使用的最佳实践,比如避免死的方法,的粒度选择等。确保回答真实可靠,结构清晰,逐步展开,让用户能逐步理解资源的工作原理。</think>资源(Resource Lock)是计算机系统中用于协调多线程/多进程访问共享资源的核心同步机制。其核心原理是通过“互斥访问”和“有序协调”来保证数据一致性。以下是分步解析: --- ### 一、资源的核心目标 1. **互斥性**:同一时刻只允许一个线程访问共享资源 2. **可见性**:确保状态变化对所有线程立即可见 3. **有序性**:控制线程访问资源的顺序 --- ### 二、基础工作原理 #### 1. 状态管理 - 每个资源维护一个二元状态: $$ \text{LockState} \in \{ \text{LOCKED}, \text{UNLOCKED} \} $$ - 通过原子操作(Atomic Operations)实现状态变更: ```c // 伪代码示例 while (atomic_compare_and_swap(&lock, UNLOCKED, LOCKED) != SUCCESS) { // 等待或执行其他策略 } ``` #### 2. 临界区保护 ```plaintext 线程A | 共享资源 | 线程B ------------------|-------------------|------------------ 申请 | | ↓ (获得) | 进入定状态 | 访问/修改资源 | | 尝试申请 → 阻塞 释放 | 恢复未定状态 | | | ↓ (获得) | | 访问/修改资源 ``` --- ### 三、关键技术实现 #### 1. 硬件支持 - **原子指令**:如x86的`LOCK`前缀指令、CAS(Compare-And-Swap) $$ \text{CAS}(addr, expected, new) = \begin{cases} \text{true} & \text{if } *addr = expected \\ \text{false} & \text{otherwise} \end{cases} $$ - **内存屏障**(Memory Barrier):保证指令执行顺序 #### 2. 操作系统支持 - **系统调用**:如futex(Fast Userspace Mutex) - **调度干预**:对等待线程进行休眠/唤醒操作 #### 3. 的实现类型 | 类型 | 特点 | 适用场景 | |--------------|-------------------------------|---------------------| | 自旋 | 忙等待(Busy-Wait) | 短期等待、内核态操作 | | 互斥 | 线程休眠+唤醒 | 用户态长等待操作 | | 读写 | 区分读/写操作 | 读多写少场景 | | 条件变量 | 配合谓词判断 | 复杂同步逻辑 | --- ### 四、典型工作流程(以互斥为例) 1. **加请求** - 检查当前状态 - 若未定→立即获得 - 若已定→进入等待队列 2. **竞争处理** - 通过队列管理等待线程 - 使用信号量或条件变量实现等待 3. **释放** - 原子操作修改状态 - 唤醒等待队列中的线程 --- ### 五、关键问题与优化 #### 1. 性能瓶颈 - **粒度**:粗粒度(简单但低效) vs 细粒度(复杂但高效) - **争用**:通过并发数据结构减少争用 #### 2. 死预防 - 遵循顺序规则 - 使用超时机制(tryLock) - 死检测算法 --- ### 六、现代扩展形式 1. **分布式**:基于Redis/ZooKeeper实现跨进程 2. **无编程**:通过CAS实现非阻塞算法 3. **事务内存**:硬件级原子操作支持 --- 通过这种分层控制机制,资源在保证正确性的同时,最大限度提升系统并发性能。实际使用时需根据具体场景(等待时间、并发强度、资源类型)选择合适的策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值