【【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方法只有声明,没有方法体(即没有{}包裹的实现部分)。例如图中的tryMonitorEnter、throwException、compareAndSwapObject等方法,只有方法签名,没有具体的实现代码。 - 常用于底层操作:一般用于需要访问操作系统底层功能、硬件资源,或者需要高性能操作的场景。比如图中用于实现原子操作的
compareAndSwap系列方法,调用了系统的cmpxchg指令,这类原子操作在并发编程中用于保证操作的原子性,其底层实现依赖于硬件和操作系统的支持,所以用native方法来实现。
AQS (AbstractQueuedSynchronizer) 概述
AQS是Java并发包(JUC)的核心框架,用于构建锁和其他同步器(如ReentrantLock、CountDownLatch等)。它通过一个FIFO队列管理线程的排队与唤醒,并提供了模板方法供子类实现。
AQS 核心数据结构
AQS内部维护一个双向链表(CLH队列)和一个状态变量(state):
state:同步状态,子类通过覆盖tryAcquire/tryRelease等方法定义其语义。Node:队列节点,包含线程引用、等待状态(CANCELLED、SIGNAL等)。
关键源码分析
同步状态管理
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 设计要点
- 模板方法模式:子类只需实现
tryAcquire、tryRelease等钩子方法,AQS处理队列管理和线程阻塞/唤醒。 - 公平性与非公平性:通过调整
tryAcquire的实现顺序(如先检查队列或直接CAS)控制公平性。 - 性能优化:通过自旋尝试减少线程切换开销,使用
CAS操作保证原子性。
AQS的设计体现了"分离变化点"的原则,将通用队列管理逻辑与特定同步语义解耦,是JUC中多数同步工具的基础实现。
462

被折叠的 条评论
为什么被折叠?



