并发编程AQS源码分析

打印 上一主题 下一主题

主题 914|帖子 914|积分 2742

并发编程AQS源码分析

AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。它是一个Java提高的底层同步工具类,比如CountDownLatch、ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的
简单来说:是用一个int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态对象
一个是 state(用于计数器,为0时释放锁)
一个是线程标记(当前线程是谁加锁的),
一个是阻塞队列Node(用于存放其他未拿到锁的线程)
例子:线程A调用了lock()方法,通过CAS将state赋值为1,然后将该锁标记为线程A加锁。如果线程A还未释放锁时,线程B来请求,会查询锁标记的状态,因为当前的锁标记为 线程A,线程B未能匹配上,所以线程B会加入阻塞队列,直到线程A触发了 unlock() 方法,这时线程B才有机会去拿到锁,但是不一定肯定拿到
源码分析阶段

直接看ReentrantLock中的lock方法加锁是如何使用到AQS的、ReentrantLock分为公平锁和非公平锁、默认是非公平锁
公平锁:按照队列先进先出的顺序进行加锁解锁
非公平锁:哪个线程先抢到锁就是哪个的、有可能造成某一个线程一直抢不到锁
非公平锁
  1. // 非公平锁
  2. final void lock() {
  3.     // 进行cas操作、state值为0则赋值为1、成功获取锁
  4.     if (compareAndSetState(0, 1))
  5.         // 设置线程标记、线程标记用来检测是否是重入锁
  6.         setExclusiveOwnerThread(Thread.currentThread());
  7.     else
  8.         // 调用AQS中的acquire方法、在调用ReentrantLock类下定义的Sync类中的nonfairTryAcquire方法进行具体的锁操作细节
  9.         // 传参1、里面一直进行for循环CAS赋值、直到哪个线程抢到返回
  10.         acquire(1);
  11. }
  12. // acquire方法是操作state状态位的方法、通过tryAcquire里面的CAS原子操作检测锁状态
  13. public final void acquire(int arg) {
  14.     // tryAcquire调用nonfairTryAcquire方法
  15.     if (!tryAcquire(arg) &&
  16.         // addWaiter: 根据给定模式创建一个当前线程的Node节点并返回、当前是创建一个独占锁的Node节点
  17.         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  18.         // 中断当前线程
  19.         selfInterrupt();
  20. }
  21. final boolean nonfairTryAcquire(int acquires) {
  22.     // 先获取当前线程标识符、用于检测当前对象的线程标识符是否是同一个、同一个则为可重入锁、可直接返回、不是则返回后调用acquireQueued
  23.     final Thread current = Thread.currentThread();
  24.     // 获取计数器、表明当前线程重入了多少次锁
  25.     int c = getState();
  26.     // 为0则代表当前线程的锁全部解锁完毕
  27.     if (c == 0) {
  28.         // 给state计数器加1、代表上锁
  29.         if (compareAndSetState(0, acquires)) {
  30.             // 设置对象的线程标识符为当前线程
  31.             setExclusiveOwnerThread(current);
  32.             // 直接返回
  33.             return true;
  34.         }
  35.     }
  36.     // 代表当前有线程在使用该对象锁、检测是否是同一线程、是则进入、为可重入锁
  37.     else if (current == getExclusiveOwnerThread()) {
  38.         // 计算state
  39.         int nextc = c + acquires;
  40.         // 小于0则代表锁过多、即0x7fffffff + 1为负数
  41.         if (nextc < 0) // overflow
  42.             throw new Error("Maximum lock count exceeded");
  43.         // 赋值state
  44.         setState(nextc);
  45.         // 直接返回
  46.         return true;
  47.     }
  48.     // 返回false、表示当前对象锁被其他线程占用了、需要等到加锁的线程解锁才进行抢占
  49.     return false;
  50. }
  51. //  根据给定模式创建一个当前线程的Node节点并返回
  52. private Node addWaiter(Node mode) {
  53.     // 根据当前线程创建一个Node节点、并传入模式(独占锁、共享锁)
  54.     Node node = new Node(Thread.currentThread(), mode);
  55.     // 获取队列尾部Node节点
  56.     Node pred = tail;
  57.     // 检测队列是否存在尾部节点、即是否存在节点
  58.     if (pred != null) {
  59.         // 新创建的Node节点上一个节点设为队列的尾部节点、然后下面直接将新创建节点插入队列尾部
  60.         node.prev = pred;
  61.         // 通过cas操作更换节点顺序、即新创建的节点为尾部、原先尾节点的下一个节点设置为当前新创建的Node节点
  62.         if (compareAndSetTail(pred, node)) {
  63.             pred.next = node;
  64.             return node;
  65.         }
  66.     }
  67.     // 将新创建的Node节点插入队列尾部
  68.     enq(node);
  69.     return node;
  70. }
  71. // 里面for死循环无限调用nonfairTryAcquire方法检测锁是否被释放、然后进行抢占加锁
  72. final boolean acquireQueued(final Node node, int arg) {
  73.     // 错误位、当finally执行时该变量为true、则代表有异常发生、取消当前的操作
  74.     boolean failed = true;
  75.     try {
  76.         boolean interrupted = false;
  77.         for (;;) {
  78.             // 返回Node节点的上一个节点
  79.             final Node p = node.predecessor();
  80.             // 检测是否是头节点、是的话在进行cas检测锁是否解锁在抢占
  81.             if (p == head && tryAcquire(arg)) {
  82.                 // 抢占到锁了、设置当前线程的Node节点为等待队列的头节点
  83.                 setHead(node);
  84.                 p.next = null; // help GC
  85.                 failed = false;
  86.                 return interrupted;
  87.             }
  88.             if (shouldParkAfterFailedAcquire(p, node) &&
  89.                 // 检测当前线程是否调用了interrupt中断线程方法、并且检测状态位进行阻塞、调用LockSupport.park(this)方式阻塞自己
  90.                 parkAndCheckInterrupt())
  91.                 // 如果循环走到这里在return返回的话、代表线程将中断
  92.                 interrupted = true;
  93.         }
  94.     } finally {
  95.         if (failed)
  96.             cancelAcquire(node);
  97.     }
  98. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊雷无声

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表