AQS的各种锁总结,概念篇

同步队列:

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

 

独占锁:

  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)唤醒所有后继节点

重入锁:

Java中对ReentrantLock和synchronized都是可重入锁,synchronized由jvm实现可重入即使,ReentrantLock都可重入性基于AQS实现。

同时,ReentrantLock还提供公平锁和非公平锁两种模式。
 

读写锁:

  1. 获取当前同步状态
  2. 计算高16为读锁状态+1后的值
  3. 如果大于能够获取到的读锁的最大值,则抛出异常
  4. 如果存在写锁,并且当前线程不是写锁的获取者,则获取读锁失败
  5. 如果上述判断都通过,则利用CAS重新设置读锁的同步状态

 

  1. 获取同步状态,并从中分离出低16为的写锁状态
  2. 如果同步状态不为0,说明存在读锁或写锁
  3. 如果存在读锁(c !=0 && w == 0),则不能获取写锁(保证写对读的可见性)
  4. 如果当前线程不是上次获取写锁的线程,则不能获取写锁(写锁为独占锁)
  5. 如果以上判断均通过,则在低16为写锁同步状态上利用CAS进行修改(增加写锁同步状态,实现可重入)
  6. 将当前线程设置为写锁的获取线程
     

你可能感兴趣的:(java)