大厂面试必会:AQS LockSupport Unsafe之间的关系

概述

AQS 是实现各种业务 Lock 的基础框架,例如ReentrantLock的实现底层就是使用 AQS。我们可以参考ReentrantLock来实现自己特定需求的 Lock 逻辑。

AQS 框架本身依赖两个强有力的工具,Unsafe 和 LockSupport,可以说是左膀右臂。

因此理解 AQS 中是如何使用 Unsafe 和 LockSupport 的,有助于我们理解各类 Lock 的底层实现原理,也可以在面试时系统地回答 AQS 相关的问题。

AQS与Unsafe

AQS 中对 Unsafe 的使用,主要是 CAS 操作。
例如:

/**
* CAS head field. Used only by enq.
* 入队时 CAS 修改队列 head
*/
private final boolean compareAndSetHead(Node update) {
	return unsafe.compareAndSwapObject(this, headOffset, null, update);
}

/**
* CAS tail field. Used only by enq.
* 入队时 CAS 修改队列 tail
*/
private final boolean compareAndSetTail(Node expect, Node update) {
	return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}

AQS 与 LockSupport

AQS 里对 LockSupport 的使用,主要是用于将入队后的线程 park,以及unpark 队列中某个线程。

// 未获取到锁的线程被包装成 Node,然后加入队列中
// 进入队列的 Node 如果处在 head,则尝试抢锁一次
// 若不在 head 位置或在 head 抢锁不成功,则进入 park
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)) { // 前一个节点是head,则尝试抢锁一次
				setHead(node); // 抢锁成功,自己变为 head
				p.next = null; // help GC
				failed = false;
				return interrupted;
			}
			if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) // 若 Lock 的逻辑是抢锁失败后 park,则这里让线程进入 park
				interrupted = true;
		}
	} finally {
		if (failed)
			cancelAcquire(node);
	}
}

// 入队的线程 park
private final boolean parkAndCheckInterrupt() {
	LockSupport.park(this);
	return Thread.interrupted();
}

LockSupport 与 Unsafe

LockSupport的两个核心方法park()和unpark(Thread thread),内部都是通过 Unsafe 的方法来实现。

// LockSupport 的 park 方法直接调用 Unsafe#park
public static void park() {
	UNSAFE.park(false, 0L);
}
// LockSupport 的 unpark 方法直接调用 Unsafe#unpark
public static void unpark(Thread thread) {
	if (thread != null)
		UNSAFE.unpark(thread);
}

Unsafe park unpark 原理

在Linux系统下,是用的Posix线程库pthread中的mutex(互斥量),condition(条件变量)来实现的。且在 park unpark 过程中,保护了一个_counter的变量。

当调用park时,先尝试能否直接拿到“许可”,即判断_counter>0时,如果成功,则把_counter设置为0,并返回。否则进入等待。

当调用 unpark 时,直接设置_counter为1,再unlock mutex返回。如果_counter之前的值是0,则还要调用pthread_cond_signal唤醒在park中等待的线程。

源码讲解可参考LockSupport(park/unpark)源码分析

LockSupport 与 wait notify 的区别

  1. wait notify 必须在 synchronized 获取锁后调用
  2. notify notifyAll 无法精确唤醒某一个线程,unpark(Thread)可以做到
  3. LockSupport#unpark(Thread)可以先于该 Thread park()前执行,这种情况下该 Thread park()时立即返回(因为底层_counter>0)

【参考】

  1. https://www.jianshu.com/p/e3afe8ab8364
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hugo Lei

赏你了,我的一点心意

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值