1.  前言

Java中好多地方用到AbstractQueuedSynchronizer(PS:简称AQS),比如ReentrantLock、线程池,这部分在面试的时候也经常被问到,今天以ReentrantLock为例,通过源码来加深对AQS的理解

2.  lock

通常,我们的用法是这样的:

那么lock(),unlock()到底做了什么,我们并不知晓,接下来一步一步揭开她的神秘面纱

2.1.  lock

可以看到,ReentrantLock默认是NonfairSync(非公平锁)

2.2.  NonfairSync

可以看到,首先判断同步状态是否是0,如果是0则临界资源没有被占用,并且将状态设为1,当前线程获得这个资源,可以访问;否则,调用acquire(1)方法尝试获取资源的访问控制权。

2.3.  tryAcquire

尝试再获取一次

如果当前同步状态是0,则跟之前相同,将状态置为1,当前线程获得访问权,返回true

如果当前资源的所有者线程就是当前线程,则状态加1,当前线程仍然获得访问权,返回true

如果以上情况都不是(PS:资源被别的线程占用着),则返回false

2.4.  acquireQueued

如果上一步tryAcquire方法返回false,则继续调用acquireQueued方法将线程添加到队列中(PS:链表的尾部)

在添加到链表之前,先封装成Node对象

将当前线程构造成Node对象,节点的模式是排它的,然后将其加到链表的尾部(或是说叫队列),最后将该结点返回

这里有两个特殊的节点:等待队列的头结点 和 等待队列的尾结点

接下来,将刚才构造的节点加到队列中

如果当前节点的前驱节点是头结点(head),并且当前节点再次尝试获取资源(tryAcquire方法),恰好成功了,于是皆大欢喜

如果当前节点的前驱节点既不是head,而且当前节点也没有抢占到资源,则循环直到前驱节点的状态变成SIGNAL,则挂起当前线程

3.  unlock

如果资源的所有者线程不是当前线程的话,则抛出异常

如果当前资源同步状态减1恰好是0,则成功释放,同步状态置为0,资源所有者线程置为null,唤醒head的后继节点

4.  小结

加锁

1、默认非公平锁(PS:当前线程不是直接加到等待队列中,而是先尝试获取一次,如果不成功则加到队列中,在加的时候还会尝试一次)

2、临街资源有一个同步状态,0表示当前没有线程占用,则可以直接可以获取到资源(加锁成功)

3、如果资源所有者线程就是当前线程,则状态加1,仍然可以获得锁

3、加锁成功以后,将同步状态置为1,资源所有者线程置为当前线程

4、不成功,则封装成Node加入到等待队列(链表)中,此时,还会像前面一样再尝试(抢占)一次

5、抢占不成功,加到等待队列中,线程挂起

解锁

1、资源所有者线程不是当前线程,则抛异常

2、唤醒下一个结点

AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)的更多相关文章

  1. Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式

    在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概 ...

  2. AbstractQueuedSynchronizer源码分析

    AbstractQueuedSynchronizer源码分析 前提 AQS(java.util.concurrent.locks.AbstractQueuedSynchronizer)是并发编程大师D ...

  3. Java并发系列[3]----AbstractQueuedSynchronizer源码分析之共享模式

    通过上一篇的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取.在共享模式下获取锁的方式也是这三种,而且基本上都是大同小异,我们搞清楚了一种就能很快 ...

  4. Java并发系列[4]----AbstractQueuedSynchronizer源码分析之条件队列

    通过前面三篇的分析,我们深入了解了AbstractQueuedSynchronizer的内部结构和一些设计理念,知道了AbstractQueuedSynchronizer内部维护了一个同步状态和两个排 ...

  5. 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 百篇博客分析OpenHarmony源码 | v27.02

    百篇博客系列篇.本篇为: v27.xx 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立贞 ...

  6. 鸿蒙内核源码分析(自旋锁篇) | 当立贞节牌坊的好同志 | 百篇博客分析OpenHarmony源码 | v26.02

    百篇博客系列篇.本篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 当立贞节牌坊的好同志 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 当立贞节牌坊 ...

  7. [源码分析]ReentrantLock & AbstractQueuedSynchronizer & Condition

    首先声明一点: 我在分析源码的时候, 把jdk源码复制出来进行中文的注释, 有时还进行编译调试什么的, 为了避免和jdk原生的类混淆, 我在类前面加了"My". 比如把Reentr ...

  8. 【JDK】JDK源码分析-ReentrantLock

    概述 在 JDK 1.5 以前,锁的实现只能用 synchronized 关键字:1.5 开始提供了 ReentrantLock,它是 API 层面的锁.先看下 ReentrantLock 的类签名以 ...

  9. 多线程之美5一 AbstractQueuedSynchronizer源码分析<一>

    AQS的源码分析 目录结构 1.什么是CAS ? 2.同步器类结构 3.CLH同步队列 4.AQS中静态内部类Node 5.方法分析 ​ 5.1.acquire(int arg ) ​ 5.2.rel ...

  10. Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析

    经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...

随机推荐

  1. jmeter主要函数助手功用说明

    jmeter中虽然有很多的插件,但是有些需要安装,有些具有一定的局限性.函数助手是一个快捷的工具库.下面记录一下函数助手中一些主要的函数的使用方法. 注:不内容中所有的实例均基于3.2记录 1._Be ...

  2. OI退役

    OI退役 by war 其实初中时就想自学编程,但是这个愿望并没有很强烈,加上中考的压力就没有实践.在中考前夕看到的现在的机房,哇,计算机奥赛.但是在机房里的可能是一神和奥神,也许有凯哥. 高一寒假学 ...

  3. 简单的NIO使用实例

    public class ThreadTest_2 { public static void main(String[] args) { Thread downloaderThread = null; ...

  4. NOIP2008 立体图

    题目描述 小渊是个聪明的孩子,他经常会给周围的小朋友们将写自己认为有趣的内容.最近,他准备给小朋友们讲解立体图,请你帮他画出立体图. 小渊有一块面积为m*n的矩形区域,上面有m*n个边长为1的格子,每 ...

  5. # xrdp 在linux deploy 折腾记录

    xrdp 在linux deploy 折腾记录 1. xrdp connect fail vnc 不管用tiger or vnc4server都可以正常用,但xrdp无法连接 netstat -tnl ...

  6. 将JSON格式数据转换为javascript对象 JSON.parse()

    <html><body><h2>通过 JSON 字符串来创建对象</h3><p>First Name: <span id=" ...

  7. Hbase 技术细节笔记(下)

      原文地址:https://cloud.tencent.com/developer/article/1006044 四.RegionServer的故障恢复 我们知道,RegionServer的相关信 ...

  8. Oracle DBLINK的相关知识整理

    一.DBLINK(Database Link)概念 dblink,顾名思义就是数据库的链接.当我们要跨本地数据库访问另一个数据库中的表的数据时,在本地数据库中就必须要创建远程数据库的dblink,通过 ...

  9. Chrome 启动全屏,并可以F11退出

    新建start.bat文件,作用,打开浏览器,并模拟按下F11,全屏,内容如下: start C:\Program" "Files" "(x86)\Google ...

  10. 修改 Docker 的 daemon.json后启动失败

    创建Harbor要把register 换成Harbor地址 vim /etc/docker/daemon.json添加{ "insecure-registries":[" ...