IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

【Java并发编程实战】—– AQS(二):获取锁、释放锁

cmsblogs-chenssy 2016-03-14 23:37:10 累计浏览 2,881 次
本机暂存

上篇博客稍微介绍了一下AQS,下面我们来关注下AQS的所获取和锁释放。

AQS锁获取

AQS包含如下几个方法:

acquire(int arg):以独占模式获取对象,忽略中断。

acquireInterruptibly(int arg): 以独占模式获取对象,如果被中断则中止。

acquireShared(int arg): 以共享模式获取对象,忽略中断。

acquireSharedInterruptibly(int arg)以共享模式获取对象,如果被中断则中止。

tryAcquire(int arg):试图在独占模式下获取对象状态。

tryAcquireNanos(int arg, long nanosTimeout):试图以独占模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。

tryAcquireShared(int arg):试图在共享模式下获取对象状态。

tryAcquireSharedNanos(int arg, long nanosTimeout):试图以共享模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。

对于lock.lock()最终都会调用AQS的acquire()方法,Semaphore.acquire()最终会调用AQS的acquireSharedInterruptibly()方法,其中acquire()源代码如下:

publicfinalvoid acquire(int arg) {    if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

tryAcquire:去尝试获取锁,获取成功则设置锁状态并返回true,否则返回false。

addWaiter:将当前线程加入到CLH队列队尾。

acquireQueued:当前线程会根据公平性原则来进行阻塞等待,直到获取锁为止;并且返回当前线程在等待过程中有没有中断过。

selfInterrupt:产生一个中断。

其主要流程如下:

2015120400001

1、首先线程尝试获取锁,如果成功则直接返回,不成功则新建一个Node节点并添加到CLH队列中。tryAcquire尝试获取锁,addWaiter则新建节点并添加到CLH队列中。其中tryAcquire,AQS并没有提供实现,它仅仅只是抛出一个异常,具体的实现需要各个锁自己实现。

protectedboolean tryAcquire(int arg) {    thrownew UnsupportedOperationException();    }

addWaiter后面讲述。

2、acquireQueued主要功能是根据该节点寻找CLH队列的头结点,并且尝试获取锁,判断是否需要挂起,并且返回挂起标识。如下:

finalboolean acquireQueued(final Node node, int arg) {    try {        boolean interrupted = false;        for (;;) {            final Node p = node.predecessor();            if (p == head && tryAcquire(arg)) {                    setHead(node);
                    p.next = null; // help GCreturn interrupted;
                }if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (RuntimeException ex) {
            cancelAcquire(node);throw ex;
        }
    }

在acquireQueued()内部仍然调用tryAcquire()来获取锁。更多详情请参考:【Java并发编程实战】—-“J.U.C”:ReentrantLock之二lock方法分析

selfInterrupt:产生一个中断。如果在acquireQueued()中当前线程被中断过,则需要产生一个中断。

privatestaticvoid selfInterrupt() {
    Thread.currentThread().interrupt();
}

AQS锁释放

AQS释放锁的方法主要有:

release(int arg):以独占模式释放对象。

releaseShared(int arg): 以共享模式释放对象

tryRelease(int arg):试图设置状态来反映独占模式下的一个释放。

tryReleaseShared(int arg):试图设置状态来反映共享模式下的一个释放。

释放锁相对于获取锁来说还是比较简单的,其主要流程如下:

2015120400002

其代码如下(release()):

publicfinalboolean release(int arg) {    if (tryRelease(arg)) {            Node h = head;if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);returntrue;
        }returnfalse;
    }

tryeRelease():尝试释放锁,AQS也同样没有提供实现,具体实现方法要其子类自己内部实现,AQS仅仅只是抛出一个异常。

protectedboolean tryRelease(int arg) {    thrownew UnsupportedOperationException();    }

unparkSuccessor:用于唤醒节点。更多,请参考:【Java并发编程实战】—-“J.U.C”:ReentrantLock之三unlock方法分析

参考文献:

1、JAVA并发编程学习笔记之AQS源码分析(获取与释放)

同分类推荐文章

  1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
  2. Go 实验特性详解 (2026-06-21 10:05:27)
  3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

查看更多 后端 文章 →

建议继续学习

  1. 并发编程系列之一:锁的意义 (累计阅读 7,569)
  2. 无锁HashMap的原理与实现 (累计阅读 6,745)
  3. Nginx源码分析-事件循环 (累计阅读 6,258)
  4. Unix高级环境编程系列笔记 (累计阅读 5,978)
  5. 为什么在多线程程序中要慎用volatile关键字? (累计阅读 5,053)
  6. C++线程池实现原理 (累计阅读 4,786)
  7. Why C++ ? 王者归来 (累计阅读 4,494)
  8. 深入分析Volatile的实现原理 (累计阅读 4,218)
  9. 深入理解Linux用户空间的锁机制 (累计阅读 4,191)
  10. 从Go看,语言设计(二) (累计阅读 3,914)