JUC之AQS必知必会,这样说就够了...

AQS是JDK并发工具包下的一个模板类,做为并发包下的工具工具基础实现,咱们常常使用的ReentrantLock,CountDownLatch,CyclicBarrier等都是基于它实现的,而且经过它咱们能够很容易的实现本身的同步机制。bash

做为JUC中这么重要的一个类,有些东西仍是要掌握的。数据结构

主要内容:并发

  • AQS原理
  • 如何利用AQS编写本身的同步机制
  • 基于AQS的重入锁和Synchronized有什么区别
  • 从AQS源码中你学到了什么

AQS原理

JDK源码要说原理,首先要提到其内部数据结构。其内部有两个子类:工具

  • ConditionObject : 提供Java代码实现的Object.wait,notify,notifyAll等方法,而且支持屡次wait,能够看作是Object这些方法的加强版。其内部也维护了一个队列,加上AQS的队列,总共有两个队列
  • Node:AQS内部主要经过维护一个双向链表来存储参与同步机制的线程状态。双向链表基于Node节点来构建,每个Node表明一个线程
    • waitStatus: Node节点的一个熟悉,用于标记当前线程的等待状态。
      • CANCELLED:即1,表明当前线程已经被取消了
      • SIGNAL: -1,表明当前的线程等待被唤醒,即unparking
      • CONDITION: -2,表明当前线程在条件队列上等待
      • PROPAGATE:-3,当前线程节点的后续节点的acquireShare方法可以被无条件执行

另外AQS内部有一个state属性,AQS内部没有直接去操做state的地方,可是留给了咱们三个方法去操做它。主要用于标记同步器的状态学习

  • getState
  • setState
  • compareAndSetState

image-20200221093528341

AQS的队列操做

  • head执行队列的头部
  • tail执行队列的尾部
  • 队列由Node组成,Node能获取其表明的Thread

img

AQS的Condition内部队列

image-20200221092800481

简述

能提到几个点,我以为就OK了:ui

  • 双向队列
  • Node的waitStatus,大体那些
  • state操做

利用AQS编写本身同步机制

根据上面提到的原理,想要使用AQS这个类:spa

1 编写一个新类线程

2 重写AQS的模板方法code

  • tryAcquire 尝试获取独占锁,
  • tryRelease 尝试释放排它锁
  • tryAcquireShared 尝试获取共享锁,返回负数表明失败,返回0表明当前的锁获取成功,可是后续没法获取,返回正数,表明后续的节点仍然能够继续获取
  • tryReleaseShared 尝试释放共享锁
  • isHeldExclusively 是否排它状态

3 利用AQS提供的基础方法补全模板方法,tryXXX开头的方法内容,已重入锁ReentrantLock为例,比较清晰。cdn

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // c = 0表明没有线程修改过状态,那么尝试设置同步器的state。若是设置成功标记当前线程持有锁
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            
            // 若是当前的线程持有锁。state + 1
            // 不然返回false,表明没法获取
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
复制代码

4 调用AQS提供的方法来使用,三个维度:是否可中断、是否共享、是否超时

  • acquire
  • acquireInterruptibly
  • acquireShared
  • acquireSharedInterruptibly
  • tryAcquireNanos
  • tryAcquireSharedNanos
  • release
  • releaseShared

关于AQS几个Acquire方法的区别:www.jianshu.com/p/c48793646…

简单来讲就是要知道try开头的方法是要咱们本身实现的。能提到try开头的方法便可,最好了解AQS内部的方法大体有哪些

ReentrantLock和Synchronized区别

常规区别:

  • ReentrantLock更加灵活,提供了超时获取锁,可中断锁。提供了非公平锁和非公平锁,而synchronized仅仅是非公平锁。
  • 用法上,ReentrantLock必须手动释放锁,而且只能修饰代码块。而synchronized不用手动释放锁,除此以外能够修饰方法。

再问一句,ReentrantLock提供的不一样获取锁的方式,好比超时获取,可中断获取,在AQS上的体现是什么呢?

即上面说的利用的是AQS的acquire,acquireInterruptibly,tryAcquireNanos。

至因而否公平的判断则是根据当前线程前是否仍有节点在等待。

学习感觉

了解了AQS的关键点以后,代码并非很复杂,主要知识点:维护双向链表,更新链表节点的状态,Lock.park,线程中断。

若是本身之后写模板类,AQS是一个很好的参照品。

其他也没太多想说的了,源码不是很复杂,因此没有贴源码出来

相关文章
相关标签/搜索