AtomicBoolean与ReentrantLock

AtomicBoolean

主要用来解决并发编程中的线程安全问题,防止某段代码重复执行或确保某项任务只能执行一次。代码中常用来作为一个标志变量,以控制并发流程。AtomicBoolean体现的是一种无锁机制,依靠底层的高效的CAS原子操作实现,提供高效的线程安全操作。

CAS简介

CAS的核心思想是'比较和交换'

如果内存中的值与预期值相等,则将其更新为新值,否则什么都不操作。

三个操作数:内存值(V,被操作的变量存在内存中)预期值(E,操作前线程认为的值应该是此值)更新值(U,如果内存值与预期值相等则更新为此值)

比较V&E--> 如果V== E 把V的值更新为U,并返回true;--> 如果V!=E 则不更新V值,并返回false

底层是依赖CPU的硬件指令来实现。体现的是一种无锁机制,不需要通过显式的加锁解锁来控制线程的执行,避免了线程上下文切换带来的开销。

CAS的一些缺点 如ABA问题 可自行查阅。

AtomicBoolean中的几个常用API

  • get():获取当前值
  • set(boolean newValue):设置新值(非原子性)
  • compareAndSet(boolean expect, boolean update):如果当前值(V)等于预期值(E)则将其更新为更新值(U)并返回true
  • getAndSet(boolean newValue):原子性设置新值并返回旧值

AtomicBoolean中的CAS

核心方法:compareAndSet(boolean expect, boolean update)


ReentrantLock

ReentrantLock是一种基于AQS框架实现的应用,是JDK中实现并发编程的手段,它的功能类似synchronized是一种互斥锁,可以保证线程安全。

  • 可重入:同一个线程可以多次获取同一个锁而不会导致死锁
  • 公平性:可以根据线程请求锁的顺序分配(公平锁)
  • 性能上:ReentrantLock更适合复杂的并发场景

ReentrantLock锁会导致整体的吞吐量下降,队列中除了第一个线程 其他的线程都会阻塞(可能会导致其他线程饿死),cpu唤醒其他线程也会带来开销。

注意事项:

  • 确保加锁与解锁配对(解锁放在finally中确保发生异常,也能正常解锁,避免锁一直被占用)
  • 避免出现死锁:多个线程尝试获取多把锁时避免出现死锁问题
    • 锁的顺序要一致:确保不同线程获取锁的顺序相同
    • 尝试获取锁(tryLock)时 要设置等待时间,避免长时间等待
  • 不要长时间占有锁(将锁的作用返回设置到最小化,减少临界区的代码量)
  • 正确处理中断
    • ReentrantLock支持可中断锁,如果线程在等待锁时被中断,会抛出 InterruptedException要能够正确处理

将以上两种方法混合使用(实际上应该是多余的)整体代码结构如下

private ReentrantLock createXXXLock = new ReentrantLock();
private AtomicBoolean createXXXFlag = new AtomicBoolean(false);

if (createXXXFlag.get()) {
            log.info("XXX");
            return;
        }
createXXXLock.lock();
try {
    if (createXXXFlag.compareAndSet(false, true)) {
        try {
            // 业务逻辑处理
            throw new RuntimeException("模拟异常");
        } catch (Exception e) {
            log.error("xxx", e.getMessage());
        } finally {
            // 无论逻辑是否成功,确保标志位被重置
            createXXXFlag.set(false);
        }
    } else {
        log.info("xxxx");
    }
} finally {
    // 确保锁的释放
    createXXXLock.unlock();
}

代码中看到有同事这样使用,故学习一下~~~ 不喜勿喷

posted @ 2024-12-03 11:15  有点儿意思  阅读(39)  评论(0编辑  收藏  举报