锁策略、CAS 及 synchronized 优化过程

锁策略

  1. 乐观锁 (Optimistic Locking)

    • 定义: 假设并发冲突不经常发生,因此不立即加锁,而是在更新时检查是否有其他线程对数据进行了修改。
    • 实现方式: 常见的实现方式是使用版本号(Versioning)或时间戳(Timestamp),在更新操作时比较版本号或时间戳是否一致。
    • 特点: 减少了对共享资源的串行访问,适合读操作频繁、写操作少的情况。当并发冲突较少时,性能表现较好。
  2. 悲观锁 (Pessimistic Locking)

    • 定义: 假设并发冲突经常发生,因此在访问前先获取锁,防止其他线程同时修改数据。
    • 实现方式: 使用传统的锁机制,如 synchronized 关键字或者 ReentrantLock 类。
    • 特点: 确保了数据操作的原子性和一致性,适合写操作频繁的情况。但由于频繁的锁竞争可能导致性能瓶颈。
  3. 轻量级锁 (Lightweight Locking)

    • 定义: 用于优化线程对同步代码块的竞争,在竞争不激烈时避免传统锁带来的性能开销。
    • 实现方式: 主要通过CAS(Compare and Swap)操作来实现,尝试将对象的标记位从偏向锁状态升级到轻量级锁状态。
    • 特点: 减少了传统锁的互斥量,适合线程交替执行同步代码块的场景,但在竞争激烈时可能会升级为重量级锁。
  4. 重量级锁 (Heavyweight Locking)

    • 定义: 传统的互斥锁机制,当多个线程竞争同一个锁时,会导致除了拥有锁的线程外其他线程阻塞。
    • 实现方式: 使用操作系统提供的同步机制或者Java中的 synchronized 关键字。
    • 特点: 确保了临界区的互斥访问,适合高并发写操作的场景,但可能会引起线程阻塞和上下文切换的开销。
  5. 自旋锁 (Spin Lock)

    • 定义: 当线程尝试获取锁时,不会立即阻塞,而是在一定时间内反复检查锁是否可用,避免线程切换的开销。
    • 实现方式: 使用CAS操作或者其他忙等待的方式实现,通常用于临界区代码执行时间短、线程数少的情况。
    • 特点: 减少了线程切换的开销,但如果锁竞争激烈或者临界区代码执行时间长,会浪费CPU资源。
  6. 挂起等待锁 (Wait and Sleep Lock)

    • 定义: 当线程无法获取锁时,会进入休眠状态,直到锁可用或者超时。
    • 实现方式: 使用操作系统提供的等待和唤醒机制,通常通过 synchronized 关键字等实现。
    • 特点: 线程在等待锁时会释放CPU资源,适合锁竞争不激烈、等待时间较长的情况,但线程切换开销较大。
  7. 普通互斥锁 (Mutex Lock)

    • 定义: 最基本的锁类型,保证同一时刻只有一个线程能够访问共享资源。
    • 实现方式: 使用 synchronized 关键字或者ReentrantLock类等,确保临界区的互斥访问。
    • 特点: 简单易用,适合多线程对共享资源进行读写的场景,但可能导致线程阻塞。
  8. 读写锁 (Read-Write Lock)

    • 定义: 区分读操作和写操作的锁,允许多个线程同时读取共享资源,但写操作时需要独占锁。
    • 实现方式: 使用 ReentrantReadWriteLock 类实现,通过读锁和写锁进行区分。
    • 特点: 提高了读操作的并发性能,适合读操作远远超过写操作的场景,但写操作的性能会有所损失。
  9. 可重入锁 (Reentrant Lock)

    • 定义: 允许同一个线程多次获取同一把锁,不会因为重复获取而造成死锁。
    • 实现方式: 通常使用 ReentrantLock 类实现,通过记录持有锁的线程和锁的持有次数来实现。
    • 特点: 方便实现递归调用或者多个方法之间相互调用的同步,但可能导致性能稍微下降。
  10. 不可重入锁 (Non-reentrant Lock)

    • 定义: 不支持同一个线程多次获取同一把锁,如果线程尝试重复获取锁会导致死锁。
    • 实现方式: 在设计时显式避免线程再次获取锁,或者使用一些特殊的锁实现。
    • 特点: 简单且轻量,适用于不需要递归调用或者多个方法之间相互调用的同步场景。
  11. 公平锁 (Fair Lock)

    • 定义: 当多个线程竞争锁时,按照请求锁的顺序获取锁,避免某些线程长时间等待的情况。
    • 实现方式: 使用某些调度算法或者在锁的实现中对等待队列进行公平的管理。
    • 特点: 避免了线程饥饿现象,但可能导致整体吞吐量下降,适合对线程获取锁顺序有明确要求的场景。
  12. 非公平锁 (Unfair Lock)

    • 定义: 多个线程竞争锁时,不考虑请求锁的顺序,有可能新来的线程直接获取锁,破坏了等待时间长的线程优先获取的原则。
    • 实现方式: 默认的 synchronized 关键字或者一些锁的实现并没有明确的公平保证。
    • 特点: 可以提高整体的吞吐量,但容易导致某些线程长时间等待,可能出现线程饥饿的情况。

这些锁策略根据应用场景和需求的不同,选择合适的锁可以有效地提升并发程序的性能和可靠性。


CAS

  • 定义:CAS 是一种乐观锁技术,是硬件对并发操作的支持。
  • 原理:CAS 包含三个操作数:内存地址 V、旧的预期值 A、新的值 B。只有当 V 的值等于 A 时,才会将 V 的值更新为 B。否则,不做任何操作。整个比较并替换的操作是原子的。
  • 优势
    • 没有线程切换的开销,性能较高。
    • 适合并发量不是很高的情况下,能够提供比较高的性能。
  • 缺点
    • ABA 问题:如果一个值原来是 A,变成了 B,再变成了 A,CAS 无法察觉到这种变化。
    • 循环时间长开销大:如果一直没有成功,可能会导致 CPU 资源浪费。

 

synchronized 优化过程

  1. 偏向锁 (Biased Locking):

    • 定义: 偏向锁是为了优化加锁操作,当锁对象只有一个线程访问时,避免每次都执行重量级锁操作。
    • 过程: 当一个线程第一次尝试获取偏向锁时,虚拟机会在对象头部记录这个线程的标识符,以后该线程进入同步块时,无需再进行任何同步操作。
    • 适用场景: 适合只有一个线程访问同步块的情况,减少了不必要的竞争开销。
  2. 轻量级锁 (Lightweight Locking):

    • 定义: 轻量级锁是为了在线程竞争不激烈时,减少传统锁(重量级锁)的性能开销。
    • 过程: 当多个线程尝试获取同一把锁时,虚拟机使用CAS操作将对象的标志从无锁状态改为轻量级锁状态,避免了传统锁的互斥量开销。
    • 适用场景: 适合线程交替执行同步代码块的情况,提高了性能。
  3. 重量级锁 (Heavyweight Locking):

    • 定义: 当竞争激烈或者同步块执行时间长时,虚拟机会将轻量级锁升级为重量级锁,使用操作系统提供的互斥量机制。
    • 过程: 虚拟机会阻塞所有试图获取同步资源的线程,直到当前持有锁的线程释放锁。
    • 适用场景: 适合对共享资源的访问频率高、竞争激烈的情况,确保数据的安全性和一致性。
  4. 锁消除 (Lock Elimination):

    • 定义: 虚拟机在即时编译时会对一些同步代码块进行逃逸分析,如果分析结果表明只有一个线程访问该同步块,就会将锁消除。
    • 过程: 虚拟机在编译阶段会将同步块中的锁消除掉,避免不必要的同步操作。
    • 适用场景: 适合方法内部的局部对象,且不会被其他线程访问的情况,提高了代码执行的效率。
  5. 锁粗化 (Lock Coarsening):

    • 定义: 虚拟机会将多个连续的同步操作合并为一个更大的同步块,减少频繁获取释放锁带来的性能开销。
    • 过程: 虚拟机会把多个连续的加锁、解锁操作合并为一个范围更大的同步块,降低同步的频率。
    • 适用场景: 适合在循环或者迭代中多次进行同步操作的情况,减少了同步的粒度,提高了性能。

 在实际应用中,根据具体需求和性能测试结果选择合适的同步机制和优化方式是至关重要的。

  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值