前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >大厂面试必会:AQS LockSupport Unsafe之间的关系

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

作者头像
hugo_lei
发布2021-08-16 18:14:57
5390
发布2021-08-16 18:14:57
举报

文章目录

概述

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
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 概述
  • AQS与Unsafe
  • AQS 与 LockSupport
  • LockSupport 与 Unsafe
  • Unsafe park unpark 原理
  • LockSupport 与 wait notify 的区别
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档