并发设计模式实战系列(14):CAS(无锁编程)

简介: 🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第十四章,废话不多说直接开始~

 

image.gif 编辑

🌟 大家好,我是摘星! 🌟

今天为大家带来的是并发设计模式实战系列,第十四章CAS(无锁编程),废话不多说直接开始~

目录

一、核心原理深度拆解

1. CAS原子操作原理

2. 无锁编程优势

二、生活化类比:超市储物柜系统

三、Java代码实现(生产级Demo)

1. 基于AtomicInteger的计数器

2. 手动实现简单CAS

四、横向对比表格

1. 同步方案对比

2. Java原子类对比

五、高级优化技巧

1. 减少CAS冲突

2. 解决ABA问题

3. 性能监控指标

六、典型应用场景

1. 高性能计数器

2. 无锁队列实现

3. 状态机切换控制

七、陷阱与规避方案

1. ABA问题解决方案对比

2. 活锁风险示例与解决

3. 性能陷阱排查表

4. 伪共享解决方案

八、终极对比:CAS vs 锁(新增)

1. 微观性能对比(纳秒级操作)

2. 选型决策树

九、JVM层优化

1. 内存屏障与CAS的关系

2. 逃逸分析与栈上分配

3. JIT编译优化策略

十、硬件级调优

1. CPU缓存行优化

2. NUMA架构优化

3. 指令级并行优化

十一、终极性能对比

1. 不同层级优化的效果对比

2. 全链路优化示例


一、核心原理深度拆解

1. CAS原子操作原理

┌───────────────┐      ┌───────────────┐      ┌───────────────┐
│  内存值 (V)    │───>  │  预期值 (A)    │───>  │  新值 (B)      │
└───────────────┘      └───────────────┘      └───────────────┘
      │                      │                      │
      └───── 比较 V == A ─────┘                      │
            │                                      │
            ▼                                      ▼
       true: 更新V=B                        false: 操作失败

image.gif

  • 硬件支持:通过CPU的CMPXCHG指令实现(x86架构)
  • Java实现sun.misc.Unsafe类或AtomicXXX系列封装

2. 无锁编程优势

  • 无阻塞:线程失败后重试而非挂起
  • 高吞吐:减少线程上下文切换
  • 无死锁:不存在资源竞争导致的死锁

二、生活化类比:超市储物柜系统

系统组件

现实类比

CAS对应行为

储物柜状态

柜门指示灯

内存值V(红=占用/绿=空闲)

用户操作

投币使用

预期值A(必须看到绿灯)

系统响应

分配柜子并变红灯

新值B(修改为红灯状态)

冲突处理

其他人抢先投币

CAS返回false,用户重试

  • 关键特征:用户无需排队(无锁),通过不断重试保证最终成功

三、Java代码实现(生产级Demo)

1. 基于AtomicInteger的计数器

import java.util.concurrent.atomic.AtomicInteger;
public class CASCounter {
    private final AtomicInteger value = new AtomicInteger(0);
    // 无锁递增
    public int increment() {
        int oldValue, newValue;
        do {
            oldValue = value.get();      // 读取当前值(A)
            newValue = oldValue + 1;    // 计算新值(B)
        } while (!value.compareAndSet(oldValue, newValue)); // CAS操作
        return newValue;
    }
    public static void main(String[] args) {
        CASCounter counter = new CASCounter();
        
        // 模拟10个线程并发计数
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    counter.increment();
                }
            }).start();
        }
        
        // 等待所有线程完成
        try { Thread.sleep(2000); } catch (InterruptedException e) {}
        System.out.println("Final value: " + counter.value.get()); // 正确输出10000
    }
}

image.gif

2. 手动实现简单CAS

import sun.misc.Unsafe;
import java.lang.reflect.Field;
class ManualCAS {
    private volatile int value;
    private static final Unsafe unsafe;
    private static final long valueOffset;
    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);
            valueOffset = unsafe.objectFieldOffset(ManualCAS.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    public boolean compareAndSwap(int expected, int newValue) {
        return unsafe.compareAndSwapInt(this, valueOffset, expected, newValue);
    }
}

image.gif


四、横向对比表格

1. 同步方案对比

方案

原理

吞吐量

适用场景

synchronized

互斥锁

简单同步需求

ReentrantLock

可重入锁

需要高级功能(如公平性)

CAS

乐观锁

多读少写场景

volatile

可见性保证

最高

单一变量状态标志

2. Java原子类对比

类名

底层实现

特性

AtomicInteger

CAS + volatile

整型原子操作

AtomicReference

CAS + volatile

对象引用原子操作

LongAdder

分段CAS

超高并发计数场景

AtomicStampedReference

CAS + 版本号

解决ABA问题


五、高级优化技巧

1. 减少CAS冲突

// 使用ThreadLocal分散热点
ThreadLocalRandom.current().nextInt(10); // 随机退避时间
// 伪代码示例
while (!casUpdate()) {
    int backoff = ThreadLocalRandom.current().nextInt(10);
    LockSupport.parkNanos(backoff); // 随机休眠
}

image.gif

2. 解决ABA问题

AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
// 更新时检查版本号
int stamp = ref.getStamp();
String current = ref.getReference();
ref.compareAndSet(current, "B", stamp, stamp + 1);

image.gif

3. 性能监控指标

// CAS成功率统计
AtomicLong successCount = new AtomicLong();
AtomicLong totalAttempts = new AtomicLong();
public int incrementWithStats() {
    int oldValue, newValue;
    do {
        totalAttempts.incrementAndGet();
        oldValue = value.get();
        newValue = oldValue + 1;
    } while (!value.compareAndSet(oldValue, newValue));
    successCount.incrementAndGet();
    return newValue;
}

image.gif

六、典型应用场景

1. 高性能计数器

// 使用LongAdder替代AtomicLong(JDK8+)
LongAdder hitCounter = new LongAdder();
void recordHit() {
    hitCounter.increment(); // 内部使用分段CAS优化
}

image.gif

2. 无锁队列实现

// 基于AtomicReference的MPSC队列(多生产者单消费者)
class LockFreeQueue<E> {
    private static class Node<E> {
        E item;
        AtomicReference<Node<E>> next;
        Node(E item) { this.item = item; }
    }
    private final AtomicReference<Node<E>> head;
    private final AtomicReference<Node<E>> tail;
    public void enqueue(E item) {
        Node<E> newNode = new Node<>(item);
        Node<E> oldTail;
        do {
            oldTail = tail.get();
        } while (!tail.compareAndSet(oldTail, newNode));
        oldTail.next.set(newNode);
    }
}

image.gif

3. 状态机切换控制

// 使用AtomicInteger实现线程安全状态机
enum State { INIT, RUNNING, STOPPED }
AtomicInteger currentState = new AtomicInteger(State.INIT.ordinal());
boolean start() {
    return currentState.compareAndSet(
        State.INIT.ordinal(), 
        State.RUNNING.ordinal()
    );
}

image.gif


七、陷阱与规避方案

1. ABA问题解决方案对比

方案

实现方式

开销

适用场景

版本号戳

AtomicStampedReference

需要严格版本控制

布尔标记

AtomicMarkableReference

简单状态标记

延迟回收

对象池+生命周期管理

复杂对象场景

2. 活锁风险示例与解决

// 错误示例:线程间互相导致CAS失败
void transfer(AtomicInteger acc1, AtomicInteger acc2, int amount) {
    while (true) {
        int old1 = acc1.get();
        if (!acc1.compareAndSet(old1, old1 - amount)) continue;
        
        int old2 = acc2.get();
        if (!acc2.compareAndSet(old2, old2 + amount)) {
            acc1.compareAndSet(old1 - amount, old1); // 回滚导致活锁
            continue;
        }
        break;
    }
}
// 解决方案:引入随机退避
if (!acc2.compareAndSet(old2, old2 + amount)) {
    acc1.compareAndSet(old1 - amount, old1);
    Thread.sleep(ThreadLocalRandom.current().nextInt(10)); // 关键点
    continue;
}

image.gif

3. 性能陷阱排查表

现象

可能原因

排查工具

CPU占用100%

CAS自旋过多

JStack采样热点方法

操作成功率低于50%

竞争激烈

JMX监控CAS成功率

延迟波动大

缓存行伪共享

JOL工具分析对象布局

4. 伪共享解决方案

// 使用@Contended注解(JDK8+)
import jdk.internal.vm.annotation.Contended;
class CounterCell {
    @Contended  // 避免伪共享
    volatile long value;
}
// 或手动填充(兼容旧版本)
class ManualPadding {
    volatile long value;
    long p1, p2, p3, p4, p5, p6; // 填充至64字节(典型缓存行大小)
}

image.gif


八、终极对比:CAS vs 锁(新增)

1. 微观性能对比(纳秒级操作)

指标

CAS(AtomicInteger)

悲观锁(synchronized)

单线程耗时

12ns

25ns

4线程竞争耗时

180ns

600ns

16线程竞争耗时

2200ns

5000ns+

上下文切换次数

0

线程数×操作次数

测试环境:MacBook Pro M1, JDK17

2. 选型决策树

是否共享变量修改?
  ├─否 → volatile足够
  └─是 → 是否满足以下全部条件?
          ├─是 → 使用CAS
          │   ├─线程竞争适中(<20线程)
          │   ├─操作足够简单(单变量修改)
          │   └─能容忍偶尔失败
          └─否 → 选择锁
              ├─需要条件等待 → ReentrantLock
              └─简单同步 → synchronized

image.gif

九、JVM层优化

1. 内存屏障与CAS的关系

// 手动插入内存屏障示例(Unsafe类)
public class MemoryBarrierDemo {
    private volatile int flag;
    private int data;
    
    void publish() {
        data = 42;                          // 普通写
        Unsafe.getUnsafe().storeFence();    // 写屏障
        flag = 1;                           // volatile写
    }
    
    void consume() {
        while (flag != 1) { /* 自旋 */ }    // volatile读
        Unsafe.getUnsafe().loadFence();     // 读屏障
        System.out.println(data);           // 保证看到42
    }
}

image.gif

  • JVM内存屏障类型
  • LoadLoad:禁止屏障前后的读操作重排序
  • StoreStore:禁止屏障前后的写操作重排序
  • LoadStore:禁止读之后写重排序
  • StoreLoad:全能屏障(最昂贵)

2. 逃逸分析与栈上分配

// 通过局部变量避免CAS竞争
public class LocalCounter {
    public long sum() {
        final long[] localSum = new long[1]; // 栈上分配(逃逸分析优化)
        
        IntStream.range(0, 100).parallel().forEach(i -> {
            localSum[0] += i; // 线程本地计算
        });
        
        return localSum[0]; // 最终合并
    }
}

image.gif

  • 优化效果:减少对AtomicLong等CAS对象的竞争

3. JIT编译优化策略

// 热点代码的循环展开(JIT自动优化)
@Benchmark
public void testCAS(Blackhole bh) {
    AtomicInteger counter = new AtomicInteger();
    for (int i = 0; i < 1000; i++) {
        counter.incrementAndGet(); // 可能被JIT展开为批量CAS
    }
    bh.consume(counter);
}

image.gif

  • JVM参数
-XX:+UnlockDiagnosticVMOptions 
-XX:+PrintAssembly 
-XX:+LogCompilation

image.gif


十、硬件级调优

1. CPU缓存行优化

// 伪共享解决方案对比
@Contended // JDK8+官方方案(需开启-XX:-RestrictContended)
class ContendedCounter {
    volatile long value1;
    volatile long value2;
}
// 手动填充方案(兼容旧JDK)
class ManualPaddingCounter {
    volatile long value1;
    long p1, p2, p3, p4, p5, p6, p7; // 填充56字节
    volatile long value2;
}

image.gif

  • 缓存行大小检测(Linux系统):
cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size

image.gif

2. NUMA架构优化

// 绑定线程到指定CPU核(Linux)
public class NumaAffinity {
    static {
        System.loadLibrary("numa_affinity");
    }
    
    public native void bindToCore(int coreId);
    public static void main(String[] args) {
        new NumaAffinity().bindToCore(2); // 绑定到2号核
        // 执行敏感计算...
    }
}

image.gif

  • JVM参数
-XX:+UseNUMA 
-XX:+UseParallelGC

image.gif

3. 指令级并行优化

// 利用CAS批量操作减少总线锁定
class BatchUpdater {
    private final AtomicLongArray values;
    
    void batchUpdate(int[] indices, long[] updates) {
        for (int i = 0; i < indices.length; i++) {
            int idx = indices[i];
            long expect, update;
            do {
                expect = values.get(idx);
                update = expect + updates[i];
            } while (!values.compareAndSet(idx, expect, update));
        }
    }
}

image.gif

  • CPU指令优化
  • LOCK CMPXCHG16B:x86的16字节CAS指令
  • PAUSE:减少自旋时的能耗(Spin Loop Hint)

十一、终极性能对比

1. 不同层级优化的效果对比

优化层级

吞吐量提升

延迟降低

适用场景

算法优化

10x

50%

数据结构选择

JVM调优

2x

30%

内存敏感型任务

硬件感知

1.5x

20%

CPU密集型计算

指令级优化

1.2x

10%

超高频CAS操作

2. 全链路优化示例

// 终极优化版计数器(结合所有优化技术)
@Contended
public class UltimateCounter {
    @Contended
    private volatile long cell1;
    @Contended
    private volatile long cell2;
    private static final sun.misc.Unsafe UNSAFE;
    private static final long cell1Offset;
    
    static {
        try {
            Field f = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            UNSAFE = (sun.misc.Unsafe) f.get(null);
            cell1Offset = UNSAFE.objectFieldOffset(
                UltimateCounter.class.getDeclaredField("cell1"));
        } catch (Exception e) { throw new Error(e); }
    }
    public void increment() {
        long expect;
        do {
            expect = UNSAFE.getLongVolatile(this, cell1Offset);
        } while (!UNSAFE.compareAndSwapLong(
            this, cell1Offset, expect, expect + 1));
    }
}

image.gif

  • 优化要点
  1. 使用@Contended避免伪共享
  2. 直接调用Unsafe绕过AtomicLong封装
  3. getLongVolatile保证可见性
  4. 精确控制内存语义
相关文章
|
设计模式 消息中间件 监控
并发设计模式实战系列(5):生产者/消费者
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第五章,废话不多说直接开始~
447 1
|
设计模式 C++
【实战指南】设计模式 - 工厂模式
工厂模式是一种面向对象设计模式,通过定义“工厂”来创建具体产品实例。它包含简单工厂、工厂方法和抽象工厂三种形式,分别适用于不同复杂度的场景。简单工厂便于理解但扩展性差;工厂方法符合开闭原则,适合单一类型产品创建;抽象工厂支持多类型产品创建,但不便于新增产品种类。三者各有优缺点,适用于不同设计需求。
528 84
|
设计模式 缓存 安全
【高薪程序员必看】万字长文拆解Java并发编程!(8):设计模式-享元模式设计指南
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
299 0
|
设计模式 负载均衡 监控
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
447 0
|
设计模式 监控 Java
并发设计模式实战系列(1):半同步/半异步模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
615 0
|
设计模式 运维 监控
并发设计模式实战系列(4):线程池
需要建立持续的性能剖析(Profiling)和调优机制。通过以上十二个维度的系统化扩展,构建了一个从。设置合理队列容量/拒绝策略。动态扩容/优化任务处理速度。检查线程栈定位热点代码。调整最大用户进程数限制。CPU占用率100%
795 0
|
设计模式 消息中间件 监控
并发设计模式实战系列(3):工作队列
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第三章,废话不多说直接开始~
379 0
|
11月前
|
设计模式 缓存 安全
【JUC】(6)带你了解共享模型之 享元和不可变 模型并初步带你了解并发工具 线程池Pool,文章内还有饥饿问题、设计模式之工作线程的解决于实现
JUC专栏第六篇,本文带你了解两个共享模型:享元和不可变 模型,并初步带你了解并发工具 线程池Pool,文章中还有解决饥饿问题、设计模式之工作线程的实现
685 2
|
12月前
|
设计模式 人工智能 算法
基于多设计模式的状态扭转设计:策略模式与责任链模式的实战应用
接下来,我会结合实战案例,聊聊如何用「策略模式 + 责任链模式」构建灵活可扩展的状态引擎,让抽奖系统的状态管理从「混乱战场」变成「有序流水线」。
|
设计模式 Java 数据库连接
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
433 16