AbstractQueuedSynchronizer【抽象队列同步器】 -AQS基本原理整理记录

AQS 是什么?

  • 一个抽象类 public abstract class AbstractQueuedSynchronizer

作用:

  • 提供一个框架来实现依赖于先进先出(FIFO)等待队列->CLH变种的阻塞锁和相关的同步器(信号量、事件等)
  • Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues.
  • FIFO双向队列,AQS依赖它来完成同步状态的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。

应用:

  • 独占模式(排它) ReentrantLockreadWriteLock.writeLock()
  • 共享模式 CountDownLatchSemaphorereadWriteLock.readLock()
  • 无论是独占锁还是共享锁,本质上都是对AQS内部的一个变量state的获取。state是一个原子的int变量,用来表示锁状态资源数

AbstractQueuedSynchronizer【抽象队列同步器】 -AQS基本原理整理记录_第1张图片
图片来自-》zofun博客园

结构:

  • 一个由Node组成的 CLH双向队列,以及一个内存可见int变量 state

独占锁—实现大致流程:

  • tryAcquire,尝试获取资源,获取后CAS更新state 0->1,占有锁
  • 未占有锁线程则,进入CLH队列,加入排队
  • CAS设置当前节点waitStatus0- > -1INITIAL -> SIGNAL,表示当前线程阻塞
  • 未释放锁 - ,线程再竞争 tail尾节点 不为null,CAS更新当前线程为尾节点,调整pre、next指向,CAS compareAndSetWaitStatus(pred, ws, Node.SIGNAL)设置前节点waitStatus状态为SIGNAL
  • 未释放锁 - , 判断当前节点,前节点Node p = node.predecessor(),如果为head节点,则尝试获取资源
  • release释放锁,getState() - releases=0,更新state值为0
  • unparkSuccessor(h)唤醒下一个节点,如果 node.next ==null || s.waitStatus > 0,则从尾节点开始向前遍历, 直到找到距离head节点最近的waitStatus <=0的节点,unpark(),唤醒节点

抢锁:

  • 尝试获取资源 -》 tryAcquire(arg),这定义的一个接口方法,等待被实现,结合state 变量的值实现
//独占式
//尝试获取资源 -》 tryAcquire(arg)
public final void acquire(int arg) {
        //尝试获取资源tryAcquire(arg), 获取到则 !true ,不进入if
        //如果state已经==1,有线程占用资源后,进入等候区 addWaiter(Node.EXCLUSIVE), arg)
        if (!tryAcquire(arg) &&
        	//Node.EXCLUSIVE 标记独占式访问 
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}

tryAcquire(arg)ReentrantLock中的实现

final boolean nonfairTryAcquire(int acquires) {
       final Thread current = Thread.currentThread();
       //获取state值
       int c = getState();
       if (c == 0) {
       	   //为0则CAS更新值1,占有资源-获取锁
           if (compareAndSetState(0, acquires)) {
           		//设置exclusiveOwnerThread 当前线程
               setExclusiveOwnerThread(current);
               return true;
           }
       }
       //锁重入
       else if (current == getExclusiveOwnerThread()) {
           int nextc = c + acquires;
           if (nextc < 0) // overflow
               throw new Error("Maximum lock count exceeded");
           setState(nextc);
           return true;
       }
       return false;
}

初始化队列-》进入等候区

private Node addWaiter(Node mode) {
		//封装当前线程-》node
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        //首次节点为null ->enq(node)
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //初始化节点
        enq(node);
        return node;
}

 /* 
 	Inserts node into queue, initializing if necessary. See picture above.
    Params:
    node – the node to insert
    Returns:
    node's predecessor
 */   
 private Node enq(final Node node) {
 		// 如果CAS设置未成功则死循环
        for (;;) {
            Node t = tail;
            // 如果尾节点为空,说明CLH队列为空,需要初始化
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                 // 设置当前节点的前驱节点
                node.prev = t;
                 // CAS设置当前节点为尾结点
                if (compareAndSetTail(t, node)) {
                     // 设置后驱节点
                    t.next = node;
                    return t;
                }
            }
        }
}

资源锁获取

  • 首先获取当前节点的先驱节点,如果先驱节点是头结点的并且成功获得同步状态的时候(if (p == head && tryAcquire(arg))),当前节点所指向的线程能够获取锁
final boolean acquireQueued(final Node node, int arg) {
        // 标识资源获取是否失败
        boolean failed = true;
        try {
            // 标识线程是否中断
            boolean interrupted = false;
            //不断的自旋尝试去获取同步状态
            for (;;) {
                // 获得当前节点的前驱节点
                final Node p = node.predecessor();
                // 如果前驱节点为头结点,说明快到当前节点了,尝试获取资源
                if (p == head && tryAcquire(arg)) {
                    // 获取资源成功
                    // 设置当前节点为头结点
                    setHead(node);
                    // 取消前驱节点(以前的头部)的后节点,方便GC回收
                    p.next = null; // help GC
                    // 标识未失败
                    failed = false;
                    // 返回中断标志
                    return interrupted;
                }
                // 如果当前节点的前驱节点不是头结点或获取资源失败
                // 需要用shouldParkAfterFailedAcquire函数判断是否需要阻塞该节点持有的线程
                // 如果需要阻塞,则执行parkAndCheckInterrupt方法,并设置被中断
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            // 如果最终获取资源失败
            if (failed)
                // 当前节点取消获取资源
                cancelAcquire(node);
        }
    }
    
     //中断线程
    static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

当前线程是否阻塞判断 ,pred.waitStatus,通过前节点的 waitStatus,等待状态判断

  • false -》不需要挂起
 /** waitStatus value to indicate thread has cancelled */
 
 //在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点, 
  static final int CANCELLED =  1;
  
  /** waitStatus value to indicate successor's thread needs unparking */
  
  //只要前置节点释放锁,就会通知标识为SIGNAL状态的后续节点的线程
  //每个等待的节点会将上一个节点的状态改为SIGNAL(-1),用来标志改节点后面的节点才可以被唤醒
  static final int SIGNAL    = -1;
  
  /** waitStatus value to indicate thread is waiting on condition */
  //此节点当前在条件队列中。标记为CONDITION的节点会被移动到一个特殊的条件等待队列(此时状态将设置为0),直到条件时才会被重新移动到同步等待队列 
  static final int CONDITION = -2;
  
  /**
   * waitStatus value to indicate the next acquireShared should
   * unconditionally propagate
   */
   //acquireShared 共享模式下,PROPAGATE状态的线程处于可运行状态
  static final int PROPAGATE = -3;
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL) //如果前置节点为SIGNAL,意味着只需要等待其他前置节点的线程被释放
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
             //true 挂起线程
            return true;
        if (ws > 0) {
        	// >0 prev节点取消了排队,直接移除这个节点
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
             //采用循环,从双向列表中移除CANCELLED的节点
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
             //使用CAS将节点从INITIAL -> SIGNAL,表示当前线程阻塞
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

锁释放:

	//tryRelease(arg) 定义的一个抽象方法,由具体使用者实现,如 ReentrantLock
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

ReentrantLock 例子 -> tryRelease(arg)

  • 上锁时,会CAS设置state为1 -》 getState() =1 , releases =1 ,c =0
  • setExclusiveOwnerThread(null); 设置当前线程为null
  • setState( c ); 设置state=0
public void unlock() {
        sync.release(1);
}

public final boolean release(int arg) {
	 //更新state值,当前线程置为null
     if (tryRelease(arg)) {
         Node h = head;
         //waitStatus !=0 说明head节点后面没有在挂起等待中的后继节点了
         if (h != null && h.waitStatus != 0)
         	 //唤醒后继节点
             unparkSuccessor(h);
         return true;
     }
     return false;
}
    
protected final boolean tryRelease(int releases) {
      int c = getState() - releases;
      if (Thread.currentThread() != getExclusiveOwnerThread())
          throw new IllegalMonitorStateException();
      boolean free = false;
      if (c == 0) {
          free = true;
          setExclusiveOwnerThread(null);
      }
      //重入锁时 c!=0 ,c-1
      setState(c);
      return free;
}

唤醒后继节点

  • unparkSuccessor(h),传入当前head头节点
private void unparkSuccessor(Node node) {
        
     int ws = node.waitStatus;
     //head 头节点是当前资源占有线程,如果head节点的ws比0小, 则直接将它设为0,因为初始值就是为0
     if (ws < 0)
         compareAndSetWaitStatus(node, ws, 0);

     Node s = node.next;
     if (s == null || s.waitStatus > 0) {
         s = null;
         // 从尾节点开始向前遍历, 直到找到距离head节点最近的ws<=0的节点
         for (Node t = tail; t != null && t != node; t = t.prev)
             if (t.waitStatus <= 0)
                 s = t;
     }
     //唤醒
     if (s != null)
         LockSupport.unpark(s.thread);
}

你可能感兴趣的:(笔记,JAVA基础,java,后端,多线程)