前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >死磕 java同步系列之AQS终篇(面试)

死磕 java同步系列之AQS终篇(面试)

作者头像
彤哥
发布2019-07-08 15:21:52
3920
发布2019-07-08 15:21:52
举报
文章被收录于专栏:彤哥读源码


问题

(1)AQS的定位?

(2)AQS的重要组成部分?

(3)AQS运用的设计模式?

(4)AQS的总体流程?

简介

AQS的全称是AbstractQueuedSynchronizer,它的定位是为Java中几乎所有的锁和同步器提供一个基础框架。

在之前的章节中,我们一起学习了ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源码,今天我们一起来对AQS做个总结。

状态变量state

AQS中定义了一个状态变量state,它有以下两种使用方法:

(1)互斥锁

当AQS只实现为互斥锁的时候,每次只要原子更新state的值从0变为1成功了就获取了锁,可重入是通过不断把state原子更新加1实现的。

(2)互斥锁 + 共享锁

当AQS需要同时实现为互斥锁+共享锁的时候,低16位存储互斥锁的状态,高16位存储共享锁的状态,主要用于实现读写锁。

互斥锁是一种独占锁,每次只允许一个线程独占,且当一个线程独占时,其它线程将无法再获取互斥锁及共享锁,但是它自己可以获取共享锁。

共享锁同时允许多个线程占有,只要有一个线程占有了共享锁,所有线程(包括自己)都将无法再获取互斥锁,但是可以获取共享锁。

AQS队列

AQS中维护了一个队列,获取锁失败(非tryLock())的线程都将进入这个队列中排队,等待锁释放后唤醒下一个排队的线程(互斥锁模式下)。

Condition队列

AQS中还有另一个非常重要的内部类ConditionObject,它实现了Condition接口,主要用于实现条件锁。

ConditionObject中也维护了一个队列,这个队列主要用于等待条件的成立,当条件成立时,其它线程将signal这个队列中的元素,将其移动到AQS的队列中,等待占有锁的线程释放锁后被唤醒。

Condition典型的运用场景是在BlockingQueue中的实现,当队列为空时,获取元素的线程阻塞在notEmpty条件上,一旦队列中添加了一个元素,将通知notEmpty条件,将其队列中的元素移动到AQS队列中等待被唤醒。

模板方法

AQS这个抽象类把模板方法设计模式运用地炉火纯青,它里面定义了一系列的模板方法,比如下面这些:

代码语言:javascript
复制
// 获取互斥锁public final void acquire(int arg) {    // tryAcquire(arg)需要子类实现    if (!tryAcquire(arg) &&        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))        selfInterrupt();}// 获取互斥锁可中断public final void acquireInterruptibly(int arg)        throws InterruptedException {    if (Thread.interrupted())        throw new InterruptedException();    // tryAcquire(arg)需要子类实现    if (!tryAcquire(arg))        doAcquireInterruptibly(arg);}    // 获取共享锁public final void acquireShared(int arg) {    // tryAcquireShared(arg)需要子类实现    if (tryAcquireShared(arg) < 0)     doAcquireShared(arg);}// 获取共享锁可中断public final void acquireSharedInterruptibly(int arg)        throws InterruptedException {    if (Thread.interrupted())        throw new InterruptedException();    // tryAcquireShared(arg)需要子类实现    if (tryAcquireShared(arg) < 0)        doAcquireSharedInterruptibly(arg);}// 释放互斥锁public final boolean release(int arg) {    // tryRelease(arg)需要子类实现    if (tryRelease(arg)) {        Node h = head;        if (h != null && h.waitStatus != 0)            unparkSuccessor(h);        return true;    }    return false;}// 释放共享锁public final boolean releaseShared(int arg) {    // tryReleaseShared(arg)需要子类实现    if (tryReleaseShared(arg)) {        doReleaseShared();        return true;    }    return false;}

获取锁、释放锁的这些方法基本上都穿插在ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源码解析中了,现在看他们是不是舒服多了,如果一开始就看这些源码,难免会很晕。

需要子类实现的方法

上面一起学习了AQS中几个重要的模板方法,下面我们再一起学习下几个需要子类实现的方法:

代码语言:javascript
复制
// 互斥模式下使用:尝试获取锁protected boolean tryAcquire(int arg) {    throw new UnsupportedOperationException();}// 互斥模式下使用:尝试释放锁protected boolean tryRelease(int arg) {    throw new UnsupportedOperationException();}// 共享模式下使用:尝试获取锁protected int tryAcquireShared(int arg) {    throw new UnsupportedOperationException();}// 共享模式下使用:尝试释放锁protected boolean tryReleaseShared(int arg) {    throw new UnsupportedOperationException();}// 如果当前线程独占着锁,返回trueprotected boolean isHeldExclusively() {    throw new UnsupportedOperationException();}

这几个方法为什么不直接定义成抽象方法呢?

因为子类只要实现这几个方法中的一部分就可以实现一个同步器了,所以不需要定义成抽象方法。

总结

今天我们大概讲了下AQS中几个重要的组成部分,搞明白了这几个结构,AQS对你将没有任何秘密可言,当然面试的时候能把这几个点答清楚,面试官也会眼前一亮的。

(1)状态变量state;

(2)AQS队列;

(3)Condition队列;

(4)模板方法;

(5)需要子类实现的方法;

彩蛋

经过前面的学习,您能简要描述一下AQS获取互斥锁的大体流程吗?

这里彤哥就不作答了,相信学习完前面的内容,答这道题不是个问题了,答不上来的还需要把下面的推荐阅读好好多看几遍^^当然,能顺手点下小广告那就更完美了^^

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 彤哥读源码 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 简介
  • 状态变量state
  • AQS队列
  • Condition队列
  • 模板方法
    • 需要子类实现的方法
    • 总结
    • 彩蛋
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档