Java【多线程】(8)CAS与JUC组件

2025-04-29 0 484

Java【多线程】(8)CAS与JUC组件


目录

1.前言

2.正文

2.1CAS概念

2.2CAS两种用途

2.2.1实现原子类

2.2.2实现自旋锁

2.3缺陷:ABA问题

2.4JUC组件

2.4.1Callable接口

2.4.2ReentrantLock(与synchronized对比)

2.4.3Semaphore信号量

2.4.4CountDownLatch

3.小结


1.前言

哈喽大家好吖,不知不觉多线程这一块大骨头终于快要啃完了,今天给大家分享的是CAS以及JUC相关组件,那么废话不多说让我们开始吧。

2.正文

2.1CAS概念

核心思想:无所并发控制

CAS(Compare And Swap)是一种基于乐观锁的无锁并发控制技术。其核心逻辑可以概括为:“我认为当前值应该是A,如果是,则更新为B;否则放弃或重试”。整个过程由硬件保证原子性,无需传统锁机制。


通俗来说
假设你和同事协同编辑一份共享文档,每次保存时系统会检查:

  1. 当前内容是否和你打开时的版本一致(预期值比对)。

  2. 如果一致,允许保存;否则提示“内容已变更,请重新编辑”。
    这个过程就是CAS的核心思想——乐观锁:先操作,冲突时重试,而非直接加锁阻塞。


CAS操作的伪代码可以拆解为以下步骤,帮助理解其原子性本质:

// 伪代码:CAS操作的逻辑分解
public boolean compareAndSwap(MemoryAddress addr, int expectedValue, int newValue) {
    // 1. 读取内存当前值
    int currentValue = *addr; 

    // 2. 比较当前值与预期值
    if (currentValue != expectedValue) {
        return false; // 值已被其他线程修改,操作失败
    }

    // 3. 若值未变,执行原子性更新
    *addr = newValue;
    return true;
}

2.2CAS两种用途

2.2.1实现原子类

针对原子类,++–这样的操作是原子的,基于CAS实现,不涉及到加锁。


传统实现:

private int count = 0;  
public synchronized void increment() {  
    count++;  
}  

进阶实现: (使用Java提供的原子类)

AtomicInteger count = new AtomicInteger(0);  
public void increment() {  
    int oldValue, newValue;  
    do {  
        oldValue = count.get();  
        newValue = oldValue + 1;  
    } while (!count.compareAndSet(oldValue, newValue)); // CAS自旋  
}  

2.2.2实现自旋锁

先回顾一个上篇文章的概念:自旋锁是线程通过循环(自旋)不断尝试获取锁,而非立即阻塞。适用于锁持有时间极短的场景。

代码实现:

public class CASSpinLock {  
    private AtomicBoolean locked = new AtomicBoolean(false);  

    // 获取锁  
    public void lock() {  
        while (!locked.compareAndSet(false, true)) {  
            // 自旋:直到成功将locked从false改为true  
        }  
    }  

    // 释放锁  
    public void unlock() {  
        locked.set(false);  
    }  
}  
  • 线程竞争不激烈时(如短任务),自旋锁比系统锁(如synchronized)更高效。

  • 缺点:长时间自旋会浪费CPU资源(需根据场景权衡)。

2.3缺陷:ABA问题

ABA问题场景

  1. 线程1读取变量值为A

  2. 线程2将值改为B,随后又改回A

  3. 线程1执行CAS操作,发现当前值仍是A,误认为未被修改过,操作成功。


通俗理解:

  • 你看到自己的水杯是满的(A),去接水时离开了一会儿。

  • 期间别人喝光水(A→B)又倒满(B→A)。

  • 你回来后以为水没被喝过,直接喝下(可能喝到别人的水!)。

这里在实际场景中就是非常严重的线程安全的问题了。

解决方案: 

 

1. 版本号标记(AtomicStampedReference)
为值附加一个版本号(类似“修改次数”),CAS时同时校验值和版本号。

AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);  

// 线程1读取值和版本号  
int stamp = ref.getStamp();  
String oldValue = ref.getReference();  

// 线程2修改值并更新版本号  
ref.compareAndSet("A", "B", stamp, stamp + 1);  
ref.compareAndSet("B", "A", stamp + 1, stamp + 2);  

// 线程1尝试修改:虽然值还是A,但版本号已变,操作失败!  
boolean success = ref.compareAndSet(oldValue, "C", stamp, stamp + 1);  

2. 状态标记(AtomicMarkableReference)
用布尔值标记是否被修改过(简化版版本号)。

2.4JUC组件

2.4.1Callable接口

官方解析:Callable (Java SE 17 & JDK 17)

Callable 是 Java 并发包(JUC)中定义的接口,类似于 Runnable,但允许线程执行任务后返回结果,并可以抛出异常。

与 Runnable 的区别:

  • Runnable 的 run() 没有返回值,Callable 的 call() 可以返回泛型结果。

  • call() 可以抛出受检异常,run() 不能。

具体案例(异步运算1加到100):

Callable<Integer> task = () -> {
    int sum = 0;
    for (int i = 1; i <= 100; i++) sum += i;
    return sum;
};
FutureTask<Integer> futureTask = new FutureTask<>(task);
new Thread(futureTask).start();

// 主线程获取结果
System.out.println("计算结果:" + futureTask.get()); // 输出 5050

通过 FutureTask 包装 Callable 任务,启动线程执行后,主线程通过 futureTask.get() 等待结果返回,类似“异步任务+回调”模式。 

2.4.2ReentrantLock(与synchronized对比)

官方解析ReentrantLock (Java SE 17 & JDK 17)

ReentrantLock 是 JUC 提供的显式锁,支持可重入性、可中断锁、公平锁等特性。

特性 synchronized ReentrantLock
锁获取方式 隐式(JVM 管理) 显式(代码手动加锁/解锁)
可中断 不支持 支持 lockInterruptibly()
公平锁 不支持 支持(构造函数指定)
条件变量(Condition) 支持(newCondition()

 案例:

class BankAccount {
    private final ReentrantLock lock = new ReentrantLock();
    private int balance = 100;

    void transfer(BankAccount target, int amount) {
        lock.lock();
        try {
            if (this.balance >= amount) {
                this.balance -= amount;
                target.balance += amount;
            }
        } finally {
            lock.unlock(); // 必须手动释放锁
        }
    }
}

synchronized 的等价实现是在方法签名加 synchronized 关键字,但 ReentrantLock 更灵活:

  • 可设置超时时间(tryLock(1, TimeUnit.SECONDS))。

  • 公平锁减少线程饥饿问题。

2.4.3Semaphore信号量

官方解析:Semaphore (Java SE 17 & JDK 17)

Semaphore 用于控制同时访问某个资源的线程数量,类似“许可证发放”。

核心方法

  • acquire():获取许可证(若无可用则阻塞)。

  • release():释放许可证。

 案例:(模拟停车场)

Semaphore semaphore = new Semaphore(3); // 3 个许可证

Runnable parkAction = () -> {
    try {
        semaphore.acquire(); // 获取车位
        System.out.println(Thread.currentThread().getName() + " 停入车位");
        Thread.sleep(2000); // 停车 2 秒
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        semaphore.release(); // 释放车位
        System.out.println(Thread.currentThread().getName() + " 离开车位");
    }
};

// 启动 5 辆车尝试停车
for (int i = 0; i < 5; i++) {
    new Thread(parkAction).start();
}

Java【多线程】(8)CAS与JUC组件

2.4.4CountDownLatch

官方解析:CountDownLatch (Java SE 17 & JDK 17)

CountDownLatch 是一个同步工具,允许一个或多个线程等待其他线程完成操作。

核心方法:

  • countDown():计数器减 1。

  • await():阻塞直到计数器归零。 

public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(3); // 需要等待 3 个任务

// 资源加载任务
        Runnable loadTask = () -> {
            try {
                Thread.sleep((long) (Math.random() * 2000));
                System.out.println(Thread.currentThread().getName() + " 加载完成");
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

// 启动 3 个资源加载线程
        new Thread(loadTask, "地图").start();
        new Thread(loadTask, "音效").start();
        new Thread(loadTask, "UI").start();

// 主线程等待所有资源加载完成
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("所有资源加载完成,开始游戏!");
    }

Java【多线程】(8)CAS与JUC组件

3.小结

今天的分享到这里就结束了,喜欢的小伙伴点点赞点点关注,你的支持就是对我最大的鼓励,大家加油!

 

平台声明:以上文章转载于《CSDN》,文章全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,仅作参考。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/2301_81073317/article/details/147224005

遇见资源网 JAVA Java【多线程】(8)CAS与JUC组件 http://www.ox520.com/157764.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务