面试官问我CAS、乐观锁、悲观锁

【【Java并发】面试官问我CAS、乐观锁、悲观锁,我反手就是骑脸输出】 https://www.bilibili.com/video/BV1ff4y1q7we/?share_source=copy_web&vd_source=d7550c8f0abb247a7ef06eb74e03f91d

无锁的优点:不会使用到操作系统底层的mutex原语,减少了操作系统的内核态和用户态之间的切换,极大提升多线程并发 的性能,

缺点:无锁编程的难度很大,容易出错。 

悲观锁(如互斥锁):就是一个线程锁住了这个值,其他的线程连读值的机会都没有,所以进程的推进很慢!

无锁编程的一大特例:CAS:compare and swap (无锁)

old value 去比对当前的value值,compare之后,如果相等才swap成new value,如果compare后发现不等,就会不断地自旋(即不断地重试CAS操作)通常会配置自旋次数来防止死循环,耐心好近之后就离开。

但是这个cas函数没有进行任何的同步操作,所以多线程下无法保持一致性(在银行里A和B转账,但是两者的总金额是固定的),是线程不安全的,可能导致多线程同时占用使用资源对象。

为了避免这个线程安全问题,需要将compare和swap绑定,形成原子操作,于是各家的CPU设计中内置实现了CAS的原子化。这样子,就不需要操作系统的mutex原语来实现底层的原子化操作了,CPU原生的支持了CAS,这样子就不需要依赖锁来进行线程的同步了。

乐观锁(无锁同步 比如AtomicInteger):这其实是无锁的同步机制,但是取名中带了锁,不要误解。

使用3条线程,将一个值,从0累加到1000.

AtomicInteger底层是通过CAS来实现同步的计数器(无锁同步),在unsafe类的方法函数getAndAddInt中就调用了CompareAndSwapInt这个函数。

被native修饰的方法 说明和本地的os有关

在 Java 中,被native修饰的方法具有以下特征:

  • 实现依赖本地代码native方法的具体实现不是用 Java 代码编写的,而是依赖于底层的本地代码(如 C、C++ 等语言编写的代码)。Java 运行时通过 JNI(Java Native Interface,Java 本地接口)来调用这些本地代码实现的方法。
  • 与平台相关:由于其实现依赖于本地代码,所以native方法的行为和性能会与运行的平台(如操作系统、硬件架构)密切相关,不具备 Java 跨平台的 “一次编写,到处运行” 的特性。
  • 声明无方法体:在 Java 代码中,native方法只有声明,没有方法体(即没有{}包裹的实现部分)。例如图中的tryMonitorEnterthrowExceptioncompareAndSwapObject等方法,只有方法签名,没有具体的实现代码。
  • 常用于底层操作:一般用于需要访问操作系统底层功能、硬件资源,或者需要高性能操作的场景。比如图中用于实现原子操作的compareAndSwap系列方法,调用了系统的cmpxchg指令,这类原子操作在并发编程中用于保证操作的原子性,其底层实现依赖于硬件和操作系统的支持,所以用native方法来实现。

 

AQS (AbstractQueuedSynchronizer) 概述

AQS是Java并发包(JUC)的核心框架,用于构建锁和其他同步器(如ReentrantLock、CountDownLatch等)。它通过一个FIFO队列管理线程的排队与唤醒,并提供了模板方法供子类实现。

AQS 核心数据结构

AQS内部维护一个双向链表(CLH队列)和一个状态变量(state):

  • state:同步状态,子类通过覆盖tryAcquire/tryRelease等方法定义其语义。
  • Node:队列节点,包含线程引用、等待状态(CANCELLEDSIGNAL等)。

关键源码分析

同步状态管理
private volatile int state; // volatile保证可见性

protected final int getState() {
    return state;
}

protected final void setState(int newState) {
    state = newState;
}

protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

独占模式获取资源
public final void acquire(int arg) {
    if (!tryAcquire(arg) && // 子类实现尝试获取
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) { // 前驱是头节点且获取成功
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) && // 检查是否需要阻塞
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

共享模式释放资源
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) { // 子类实现尝试释放
        doReleaseShared(); // 唤醒后继节点
        return true;
    }
    return false;
}

private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;
                unparkSuccessor(h); // 唤醒后继线程
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;
        }
        if (h == head) break;
    }
}

实现自定义同步器示例

以下是一个基于AQS的简单不可重入锁实现:

class Mutex extends AbstractQueuedSynchronizer {
    @Override
    protected boolean tryAcquire(int acquires) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    @Override
    protected boolean tryRelease(int releases) {
        if (getState() == 0) throw new IllegalMonitorStateException();
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
}

AQS 设计要点

  1. 模板方法模式:子类只需实现tryAcquiretryRelease等钩子方法,AQS处理队列管理和线程阻塞/唤醒。
  2. 公平性与非公平性:通过调整tryAcquire的实现顺序(如先检查队列或直接CAS)控制公平性。
  3. 性能优化:通过自旋尝试减少线程切换开销,使用CAS操作保证原子性。

AQS的设计体现了"分离变化点"的原则,将通用队列管理逻辑与特定同步语义解耦,是JUC中多数同步工具的基础实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值