zoukankan      html  css  js  c++  java
  • AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)

    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、唤醒下一个结点

  • 相关阅读:
    DataBindings 与 INotifyPropertyChanged 实现自动刷新 WinForm 界面
    EasyInvoice 使用教程
    下载网页通用类
    c#操作excel的一些记录
    sql数据库基础知识整理,常用函数及常用语法
    动态行转列 pivot实现
    EFCodeFirst 数据迁移问题~
    asp.net 翻页时用ViewState保存上一页checkbox勾选的值
    参数化拼接in查询条件,个人备份
    retry
  • 原文地址:https://www.cnblogs.com/cjsblog/p/9636379.html
Copyright © 2011-2022 走看看