大白话聊聊Java并发面试问题之谈谈你对AQS的理解?
要点:
1,并发框架,实现加锁
2,status(CAS实现); 当前线程;等待队列
一、写在前面
上一篇文章聊了一下java并发中常用的原子类的原理和Java 8的优化,具体请参见文章:大白话聊聊Java并发面试问题之Java 8如何优化CAS性能?。
这篇文章,我们来聊聊面试的时候比较有杀伤力的一个问题:聊聊你对AQS的理解?
之前有同学反馈,去互联网公司面试,面试官聊到并发时就问到了这个问题。当时那位同学内心估计受到了一万点伤害。。。
因为首先,很多人还真的连AQS是什么都不知道,可能听都没听说过。或者有的人听说过AQS这个名词,但是可能连具体全称怎么拼写都不知道。
更有甚者,可能会说:AQS?是不是一种思想?我们平时开发怎么来用AQS?
总体来说,很多同学估计都对AQS有一种云里雾里的感觉,如果用搜索引擎查一下AQS是什么?看几篇文章,估计就直接放弃了,因为密密麻麻的文字,实在是看不懂!
所以,基于上述痛点,咱们这篇文章,就用最简单的大白话配合N多张手绘图,给大家讲清楚AQS到底是什么?让各位同学面试被问到这个问题时,不至于不知所措。
二、ReentrantLock和AQS的关系
首先我们来看看,如果用java并发包下的ReentrantLock来加锁和释放锁,是个什么样的感觉?
这个基本学过java的同学应该都会吧,毕竟这个是java并发基本API的使用,应该每个人都是学过的,所以我们直接看一下代码就好了:
上面那段代码应该不难理解吧,无非就是搞一个Lock对象,然后加锁和释放锁。
你这时可能会问,这个跟AQS有啥关系?关系大了去了!因为java并发包下很多API都是基于AQS来实现的加锁和释放锁等功能的,AQS是java并发包的基础类。
举个例子,比如说ReentrantLock、ReentrantReadWriteLock底层都是基于AQS来实现的。
那么AQS的全称是什么呢?AbstractQueuedSynchronizer,抽象队列同步器。给大家画一个图先,看一下ReentrantLock和AQS之间的关系。
我们来看上面的图。说白了,ReentrantLock内部包含了一个AQS对象,也就是AbstractQueuedSynchronizer类型的对象。这个AQS对象就是ReentrantLock可以实现加锁和释放锁的关键性的核心组件。
三、ReentrantLock加锁和释放锁的底层原理
好了,那么现在如果有一个线程过来尝试用ReentrantLock的lock()方法进行加锁,会发生什么事情呢?
很简单,这个AQS对象内部有一个核心的变量叫做state,是int类型的,代表了加锁的状态。初始状态下,这个state的值是0。
另外,这个AQS内部还有一个关键变量,用来记录当前加锁的是哪个线程,初始化状态下,这个变量是null。
接着线程1跑过来调用ReentrantLock的lock()方法尝试进行加锁,这个加锁的过程,直接就是用CAS操作将state值从0变为1。
如果不知道CAS是啥的,请看上篇文章,大白话聊聊Java并发面试问题之Java 8如何优化CAS性能?
如果之前没人加过锁,那么state的值肯定是0,此时线程1就可以加锁成功。
一旦线程1加锁成功了之后,就可以设置当前加锁线程是自己。所以大家看下面的图,就是线程1跑过来加锁的一个过程。
其实看到这儿,大家应该对所谓的AQS有感觉了。说白了,就是并发包里的一个核心组件,里面有state变量、加锁线程变量等核心的东西,维护了加锁状态。
你会发现,ReentrantLock这种东西只是一个外层的API,内核中的锁机制实现都是依赖AQS组件的。
这个ReentrantLock之所以用Reentrant打头,意思就是他是一个可重入锁。
可重入锁的意思,就是你可以对一个ReentrantLock对象多次执行lock()加锁和unlock()释放锁,也就是可以对一个锁加多次,叫做可重入加锁。
大家看明白了那个state变量之后,就知道了如何进行可重入加锁!
其实每次线程1可重入加锁一次,会判断一下当前加锁线程就是自己,那么他自己就可以可重入多次加锁,每次加锁就是把state的值给累加1,别的没啥变化。
接着,如果线程1加锁了之后,线程2跑过来加锁会怎么样呢?
我们来看看锁的互斥是如何实现的?线程2跑过来一下看到,哎呀!state的值不是0啊?所以CAS操作将state从0变为1的过程会失败,因为state的值当前为1,说明已经有人加锁了!
接着线程2会看一下,是不是自己之前加的锁啊?当然不是了,“加锁线程”这个变量明确记录了是线程1占用了这个锁,所以线程2此时就是加锁失败。
给大家来一张图,一起来感受一下这个过程:
接着,线程2会将自己放入AQS中的一个等待队列,因为自己尝试加锁失败了,此时就要将自己放入队列中来等待,等待线程1释放锁之后,自己就可以重新尝试加锁了
所以大家可以看到,AQS是如此的核心!AQS内部还有一个等待队列,专门放那些加锁失败的线程!
同样,给大家来一张图,一起感受一下:
接着,线程1在执行完自己的业务逻辑代码之后,就会释放锁!他释放锁的过程非常的简单,就是将AQS内的state变量的值递减1,如果state值为0,则彻底释放锁,会将“加锁线程”变量也设置为null!
整个过程,参见下图:
接下来,会从等待队列的队头唤醒线程2重新尝试加锁。
好!线程2现在就重新尝试加锁,这时还是用CAS操作将state从0变为1,此时就会成功,成功之后代表加锁成功,就会将state设置为1。
此外,还要把“加锁线程”设置为线程2自己,同时线程2自己就从等待队列中出队了。
最后再来一张图,大家来看看这个过程。
四、总结
OK,本文到这里为止,基本借着ReentrantLock的加锁和释放锁的过程,给大家讲清楚了其底层依赖的AQS的核心原理。
基本上大家把这篇文章看懂,以后再也不会担心面试的时候被问到:谈谈你对AQS的理解这种问题了。
其实一句话总结AQS就是一个并发包的基础组件,用来实现各种锁,各种同步组件的。它包含了state变量、加锁线程、等待队列等并发中的核心组件。
并发系列文章,正在更新中,欢迎关注:大白话聊聊Java并发面试问题之volatile到底是什么?大白话聊聊Java并发面试问题之Java 8如何优化CAS性能?
《大白话聊聊Java并发面试问题之谈谈你对AQS的理解?》
《大白话聊聊Java并发面试问题之公平锁与非公平锁是啥?》,敬请期待
《大白话聊聊Java并发面试问题之微服务注册中心的读写锁优化?》,敬请期待
大白话聊聊Java并发面试问题之谈谈你对AQS的理解?相关推荐
- 大白话系列之java_并发系列2-大白话聊聊Java并发面试问题之Java 8如何优化CAS性能?【石杉的架构笔记】...
Lua-面向对象中函数使用时冒号(:)和点(.)的区别 先来看一段简单的代码: local Animal = {} function Animal:Eat( food ) print("An ...
- 并发执行变成串行_大白话Java并发面试问题之Java 8如何优化CAS性能?
专注于Java领域优质技术,欢迎关注 来自:石杉的架构笔记 一.前言 这篇文章给大家聊一下java并发包下的CAS相关的原子操作,以及Java 8如何改进和优化CAS操作的性能. 因为Atomic系列 ...
- 多线程面试题_100多线程和Java并发面试问答–最终清单(PDF下载)
多线程面试题 在这篇文章中,我们将提供有关多线程和Java并发面试问答的综合文章. 编者注:并发始终是开发人员的挑战,编写并发程序可能非常困难. 引入并发时,有很多事情可能会崩溃,并且系统的复杂性会大 ...
- 【搞定 Java 并发面试】面试最常问的 Java 并发进阶常见面试题总结!
本文为 SnailClimb 的原创,目前已经收录自我开源的 JavaGuide 中(61.5 k Star![Java学习 面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.觉得内容不错 ...
- Java并发面试,幸亏有点道行,不然又被忽悠了
2019独角兽企业重金招聘Python工程师标准>>> 前言 面试Java,必然要被问Java内存模型和Java并发开发.我被问到的时候,心里慌得一批,"额,是在<T ...
- java 并发 面试_Java 并发基础常见面试题总结
1. 什么是线程和进程? 1.1. 何为进程? 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的.系统运行一个程序即是一个进程从创建,运行到消亡的过程. 在 Java 中,当我们启 ...
- Java并发面试宝典,并发相关面试再也难不倒你!
1.在java中守护线程和用户线程的区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon ...
- Java 并发面试题解
作为一名专业的 Java 开发者,如何在并发场景中写出优良的代码,是一道绕不开的坎,也是考量一个 Java 开发者功底的关键技术.因此,不难发现 Java 并发问题一直是各个大厂面试的重点之一,然而我 ...
- Java 并发编程—有锁互斥机制及AQS理论
原文作者:Java并发编程 原文地址:AQS这样学就很简单了 目录 一.有锁互斥机制 二.AQS如何实现互斥 三.结语 如果你是道格李,你要实现一套机制来保证线程互斥,你会如何实现呢?你肯定不会一上来 ...
- 第九十三期:带你聊聊 Java 并发编程之线程基础
百丈高楼平地起,要想学好多线程,首先还是的了解一下线程的基础,这边文章将带着大家来了解一下线程的基础知识. 作者:小九 01.简介 百丈高楼平地起,要想学好多线程,首先还是的了解一下线程的基础,这边文 ...
最新文章
- 经典面试题-Linux Kernel面试题
- 配置MGR时修改了/etc/hosts但映射后的hostname不起作用
- 并发编程之 Semaphore 源码分析
- 数学计算机培训反思,小学数学信息技术反思日志5
- Hive数据如何同步到MaxCompute之实践讲解
- java实验1机动车实验目的_《Java程序设计》实验指导书.doc
- springMVC中数据流解析与装载
- 重叠面积_20天津中考试卷及答案(24题重叠面积画板制作教程)
- Graph Embedding图嵌入
- UltraEdit for Mac
- 1、认识和安装MongoDB
- 2018美赛C题翻译加原文
- Velocity的使用
- dht11传感器c语言程序,树莓派 DHT11 温湿度传感器读取 C 语言版
- MySQL中的B+树索引结构
- php 按钮外观怎么改变,php 按钮样式
- 有权最短路径问题:狄克斯特拉(Dijkstra)算法 Java 实现
- HNU-电子测试平台与工具-元器件及测量基础
- postgreSQL数据类型字符串和数值相互转换
- Tensorflow faster RCNN目标检测车牌