当前位置:   article > 正文

*AQS详解_aqs中waitstatus为0或-3的意义

aqs中waitstatus为0或-3的意义

前言

AQS即AbstractQueuedSynchronizer ,提供加锁解锁的一套模板,具体实现细节由子类实现,可以通过"三板斧"的辅助概念来理解:

  • 状态 status
  • 队列 queue
  • cas

状态 state

private volatile int state;//同步状态变量

state为整个AQS的核心,是全局共享的一个状态,为保证其修改的可见性,用volatile修饰。state在不同的子类中有不同的含义。

以ReentrantLock为例,state表示该锁被线程重入的次数:当state=0时表示该锁不被任何线程持有;state=1表示当前线程持有该锁1次;state>1表示当前线程重入锁的次数;

state有三种访问方式:getState();setState();compareAndSetState(),都是原子操作。

队列

同步等待队列是一个Node节点的双向链表来实现,队列采用悲观锁的思想,即当前线程需要获取锁时就有其他线程也来获锁。因此,它会把当前线程包装成一个Node节点,放入等待队列中,当一定条件满足后,再从等待队列中移除。

  • Node节点

  1. volatile int waitStatus;//当前节点等待的状态
  2. volatile Node prev;//前驱
  3. volatile Node next;//后继
  4. volatile Thread thread;//当前节点的线程

waitStatus的状态有5种:

  1. static final int CANCELLED(1);//当前节点已取消调度,当timeout或者中断会触发更新成改状态,终态
  2. static final int SINGAL(-1);//后继节点在等待当前节点唤醒,当后继结点加入时会触发更新为改状态
  3. static final int CONDITION(-2);//结点等待在Condition上,当其他线程调用了Condition的signal()方
  4. //法后,CONDITION 状态的节点将从等待队列转移到同步队列中,等待获取同步锁
  5. static final int PROPAGATE(-3);//共享模式下,前驱结点不仅会唤醒后继结点,也有可能唤醒后继的后继
  6. 0;默认状态
  • 双向CLH链表
  1. private transient volatile Node head;
  2. private transient volatile Node tail;

head结点:是一个哑结点(dummy node),它不代表任何线程,一次head所指向的Node的thread永远是null。只有从次结点往后的所有结点才表示所有等待锁的线程。

AQS定义了两种资源共享方式:独占式(Exclusive)和共享式(Share)。

  •  独占式:只有一个线程能执行,具体的Java实现有ReentrantLock。
  •  共享式:多个线程可同时执行,具体的Java实现有Semaphore和CountDownLatch。

AQS只是一个框架,只定义了一个接口,具体资源的获取、释放都交由自定义同步器去实现。不同的自定义同步器争用共享资源的方式也不同,自定义同步器在实现时只需实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护,如获取资源失败入队、唤醒出队等,AQS已经在顶层实现好,不需要具体的同步器再做处理。

 独占:

ReentrantLock对AQS的独占方式实现为:

  • ReentrantLock中的state初始值为0时表示无锁状态。
  • 在线程执行tryAcquire()获取该锁后ReentrantLock中的state+1,这时该线程独占ReentrantLock锁,其他线程在通过tryAcquire()获取锁时均会失败,直到该线程释放锁后state再次为0,其他线程才有机会获取该锁。
  • 该线程在释放锁之前可以重复获取此锁,每获取一次便会执行一次state+1,因此ReentrantLock也属于可重入锁。但获取多少次锁就要释放多少次锁,这样才能保证state最终为0。
  • 如果获取锁的次数多于释放锁的次数,则会出现该线程一直持有该锁的情况;如果获取锁的次数少于释放锁的次数,则运行中的程序会报锁异常。
  • ReentrantLock 默认是非公平的,提高吞吐

共享:

CountDownLatch对AQS的共享方式实现为:

  • CountDownLatch将任务分为N个子线程去执行,将state也初始化为N, N与线程的个数一致,N个子线程是并行执行的,
  • 每个子线程都在执行完成后countDown()一次,state会执行CAS操作并减1。
  • 在所有子线程都执行完成(state=0)时会unpark()主线程,然后主线程会从await()返回,继续执行后续的动作。

unparkSuccessor 方法中for循环从tail开始而不是head

这个要看加入的地方

  1. private Node enq(final Node node) {
  2. for (;;) {
  3. Node t = tail;
  4. if (t == null) { // Must initialize
  5. if (compareAndSetHead(new Node()))
  6. tail = head;
  7. } else {
  8. //看这里
  9. node.prev = t;
  10. if (compareAndSetTail(t, node)) {
  11. t.next = node;
  12. return t;
  13. }
  14. }
  15. }
  16. }

新节点pre指向tail,tail指向新节点,这里后继指向前驱的指针是由CAS操作保证线程安全的。而cas操作之后t.next=node之前,可能会有其他线程进来。所以出现了问题,从尾部向前遍历是一定能遍历到所有的节点。

ABA问题

CAS 并不是万能的,CAS 更新有 ABA 问题。即 T1 读取内存变量为 A ,T2 修改内存变量为 B ,T2 修改内存变量为 A ,这时 T1 再 CAS 操作 A 时是可行的。但实际上在 T1 第二次操作 A 时,已经被其他线程修改过了。

AtomicStampedReference

对于 ABA 问题,比较有效的方案是引入版本号,内存中的值每发生一次变化,版本号都 +1;在进行CAS操作时,不仅比较内存中的值,也会比较版本号,只有当二者都没有变化时,CAS才能执行成功。 AtomicStampedReference 便是使用版本号来解决ABA问题的。类似的还有 AtomicMarkableReference , AtomicStampedReference 是使用 pair 的 int stamp 作为计数器使用, AtomicMarkableReference 的 pair 使用的是 boolean mark

ReentrantReadWriteLock

线程进入读锁的前提条件:

  • 没有其他线程的写锁,
  • 没有写请求或者有写请求,但调用线程和持有锁的线程是同一个

线程进入写锁的前提条件:

  • 没有其他线程的读锁
  • 没有其他线程的写锁

而读写锁的特性:

(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。

(2)重进入:读锁和写锁都支持线程重进入。

(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。

说明:Sync抽象类继承自AQS抽象类,Sync类提供了对ReentrantReadWriteLock的支持。

Sync类内部存在两个内部类,分别为HoldCounter和ThreadLocalHoldCounter,其中HoldCounter主要与读锁配套使用,其中,HoldCounter源码如下。

  1. static final class HoldCounter {
  2. int count = 0;
  3. // Use id, not reference, to avoid garbage retention
  4. final long tid = getThreadId(Thread.currentThread());
  5. }
  6. /**
  7. * ThreadLocal subclass. Easiest to explicitly define for sake
  8. * of deserialization mechanics.
  9. */
  10. static final class ThreadLocalHoldCounter
  11. extends ThreadLocal<HoldCounter> {
  12. public HoldCounter initialValue() {
  13. return new HoldCounter();
  14. }
  15. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/149147
推荐阅读
  

闽ICP备14008679号