Java并发-AQS及各种Lock锁的原理

什么是AQS
AQS是AbustactQueuedSynchronizer的简称,它是一个Java提高的底层同步工具类,用一个int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态。AQS的主要作用是为Java中的并发同步组件提供统一的底层支持,例如ReentrantLock,CountdowLatch就是基于AQS实现的,用法是通过继承AQS实现其模版方法,然后将子类作为同步组件的内部类。

同步队列
同步队列是AQS很重要的组成部分,它是一个双端队列,遵循FIFO原则,主要作用是用来存放在锁上阻塞的线程,当一个线程尝试获取锁时,如果已经被占用,那么当前线程就会被构造成一个Node节点假如到同步队列的尾部,队列的头节点是成功获取锁的节点,当头节点线程释放锁时,会唤醒后面的节点并释放当前头节点的引用。 

独占锁的获取和释放流程
获取

  1. 调用入口方法acquire(arg)
  2. 调用模版方法tryAcquire(arg)尝试获取锁,若成功则返回,若失败则走下一步
  3. 将当前线程构造成一个Node节点,并利用CAS将其加入到同步队列到尾部,然后该节点对应到线程进入自旋状态
  4. 自旋时,首先判断其前驱节点释放为头节点&&是否成功获取同步状态,两个条件都成立,则将当前线程的节点设置为头节点,如果不是,则利用LockSupport.park(this)将当前线程挂起 ,等待被前驱节点唤醒

释放

  1. 调用入口方法release(arg)
  2. 调用模版方法tryRelease(arg)释放同步状态
  3. 获取当前节点的下一个节点
  4. 利用LockSupport.unpark(currentNode.next.thread)唤醒后继节点(接获取的第四步) 
     

 共享锁的获取和释放流程

获取共享锁:

  1. 调用acquireShared(arg)入口方法
  2. 进入tryAcquireShared(arg)模版方法获取同步状态,如果返返回值>=0,则说明同步状态(state)有剩余,获取锁成功直接返回
  3. 如果tryAcquireShared(arg)返回值<0,说明获取同步状态失败,向队列尾部添加一个共享类型的Node节点,随即该节点进入自旋状态
  4. 自旋时,首先检查前驱节点释放为头节点&tryAcquireShared()是否>=0(即成功获取同步状态)
  5. 如果是,则说明当前节点可执行,同时把当前节点设置为头节点,并且唤醒所有后继节点
  6. 如果否,则利用LockSupport.unpark(this)挂起当前线程,等待被前驱节点唤醒

释放锁:

  1. 调用releaseShared(arg)模版方法释放同步状态
  2. 如果释放成,则遍历整个队列,利用LockSupport.unpark(nextNode.thread)唤醒所有后继节点

独占锁和共享锁在实现上的区别

  1. 独占锁的同步状态值为1,即同一时刻只能有一个线程成功获取同步状态
  2. 共享锁的同步状态>1,取值由上层同步组件确定
  3. 独占锁队列中头节点运行完成后释放它的直接后继节点
  4. 共享锁队列中头节点运行完成后释放它后面的所有节点
  5. 共享锁中会出现多个线程(即同步队列中的节点)同时成功获取同步状态的情况
     

AQS独占和共享锁,ReentantLock为独占锁。

ReentantReadWriteLock中readLock()为共享锁,writeLock()为独占锁:
读锁与读锁可以共享
读锁与写锁不可以共享
写锁与写锁不可以共享

 

展开阅读全文

没有更多推荐了,返回首页