java多线程之AQS

前言

今天从ReentryLock阅读到AQS的源码,发现看得很轻松。前提是得对 synchronized底层原理比较熟悉,其原理和AQS很像,感觉AQS就是一个基于Java的monitor实现。那么接下来我将类比monitor的结构来记录我对AQS流程的理解。

AQS

首先什么是AQS,个人认为就是一个类似monitor的结构,ReentryLock只不过利用了这个结构实现了可重入,公平与非公平的高级逻辑罢了。所以为什么说所有的锁都是基于AQS的。

参考黑马笔记的结构图来说明
在这里插入图片描述

组成部分

  • state
    • 对CAS锁了解的就很明白了,这玩意就是用来实现CAS锁的标志位,初始值为0,哪个线程用CAS将0改为1成功,则说明占锁成功
  • head,tail
    • 链表的头尾指针。维护了一个类似monitor中entrylist的链表
  • Owner
    • 与monitor的Owner作用相同,表明当前锁是哪个线程在占
  • Node
    • node节点的value值为线程id以及waitstate(黄色三角型)
    • waitstate作用在流程中说明
  • ConditionObject
    • 这不就是monitor中的waitset嘛,只是ConditionObeject中还可以有多个子waitset,这样能够细分waitset,叫醒的时候可以只叫醒一小部分。你看这不就把syn与AQS的区别找到了吗

在这里插入图片描述
(一定要看着图去想流程,会记得特别牢噢)

整体流程

  1. 多线程同时访问AQS,需要先抢锁
  2. 使用CAS锁的方式去抢,抢到锁的线程将自己的id写入ownerThread中,抢不到锁就像monitor机制一样,被加入entrylist阻塞等待
  3. 抢到锁的线程执行完任务后释放锁,并唤醒entrylist的线程进行抢锁
  4. 如果调用await方法,就会将owner进程放入对应的condition中,等待被唤醒

上述方法就是AQS的流程了,你会发现这不就是monitor的流程吗?是的没错,那么区别在哪呢?我们只需要对比着去找区别就能很好的理解AQS巧妙之处了。所以我带着找区别这个问题去看了源码,对上述过程进行细化

  • 在抢锁上有什么不同?
    • 对于syn的源码我还没看,只能知道AQS怎么抢锁的
    • 通过CAS的方式抢锁,但又于单纯的CAS锁不同,我们实现CAS锁的时候如果抢不到锁则让线程自旋或者wait,这里直接加入等待队列中
  • 等待队列怎么维护的?数据结构是什么?
    • 等待队列是一个链表,头尾指针作为AQS的成员变量
    • 要思考的是,怎么保证链表是线程安全的?
      • 查看源码会发现,head与tail两个指针都是volatile修饰的,即保证了可见性
      • 当添加节点到链表中时,需要改变指针的指向,为了保证线程安全,用的是Unsafe类的CAS方法来改变指针指向
      • 所以也就是说volatile+CAS保证了并发操作头尾指针时是线程安全的
  • 将队列中的线程阻塞用的是什么方法?wait吗?为什么?
    • 是Unsafe类的park方法,唤醒用的unpark
  • 怎么唤醒entrylist中的线程?所有都唤醒吗?
    • 唤醒链表中第一个线程
  • 怎么实现可重入的?
    • 线程A第一次抢到锁后,state变为1,且ownerThread为A的Id
    • 线程A第二次抢锁时,发现state已经是1,则查看ownerThread是否为自己Id,是的话也能够拿到锁,并且将state+1
    • 释放的时候需要state-1
  • 非公平锁指的是?怎么实现非公平的?
    • 非公平指的是entrylist中的第一个线程被唤醒后正要抢锁,此时不在entrylist中的新的线程C来与之抢锁,线程C有可能可以抢到锁。注意,进入entrylist的线程是要排队的,非公平是新的和旧的抢。这个源码中很清晰
    • 实现方法就是在抢锁的时候不需要判断entrylist是否存在线程
  • 公平锁是指?怎么实现公平的?
    • 公平锁就是新来的也得乖乖去entrylist排队
    • 实现方法就是在抢锁时判断entrylist是否存在线程,存在则不允许抢,需要去排队
    • ReentryLock就是基于AQS去实现了上述可重入以及非公平和公平锁的逻辑

上述都是自己提的问题并且去看源码的一些细节得到的,主要是记录给自己看的,只需要心中有monitor结构,去看AQS就很轻松,只是细节需要搞清楚罢了。那么AQS拿下后,其他的Lock自然就拿下了

噢最后一点就是,通过阅读AQS源码,可以回答一个问题
synchronized 与 ReentryLock有什么区别?

  • synchronized改进后有偏向锁,轻量锁,重量锁三个阶段,从结构上,重量锁与ReentryLock比较相似(monitor结构)。也就意味着其实冲突不大的时候,我们应该使用synchronized的偏向锁和轻量锁,而不是无脑ReentryLock,然后嫌弃synchronized太重了。
  • 那么比较的话也就是拿synchronized的重量锁与ReentryLock比
  • 从结构图可以明显的看到,AQS的ConditionObject相比于原monitor中的waitset虽然作用相同,但是进行了更细的划分,让线程调度更灵活
  • 还有一点没说,ReentryLock实现了可中断的方法还能超时,syn不行,也体现了RLock更灵活
  • syn是基于JVM的底层实现,Lock是JAVA编写的

源码浅析

补充一些源码记录

Sync

无论是ReentrantLock还是CountDownLauch等实现,都是基于AQS的,对应与都有一个Sync内部类,该内部类继承了AQS,ReentrantLock的所有操作都是通过Sync完成
在这里插入图片描述

ReentrantLock

上锁

    public void lock() {
        sync.lock();
    }

下锁

    public void unlock() {
        sync.release(1);
    }

lock在Sync中为抽象方法,设计了公平与非公平锁两个实现类进行实现
在这里插入图片描述
对于非公平的lock实现如下,CAS抢锁,设置owner线程,如果CAS失败,走acquire的可重入判断

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值