java aqs原理浅析
来源:互联网 发布:公路工程试验软件 编辑:程序博客网 时间:2024/05/16 15:32
前言
aqs是指java.util.concurrent.locks包下的类AbstractQueuedSynchronizer,这个类是java.util.concurrent包的基础,同步工具类Semaphore、CountDownLatch、ReentrantLock、ReentrantReadWriteLock都是基于aqs实现。在aqs类上的第一段注释如下 ,英语不溜就不作翻译了,大概意思是aqs是实现阻塞锁和一些基于FIFO(先进先出)同步器的基类,类中包含一个int类型状态state代表着锁被获取和释放:
/**
* Provides a framework for implementing blocking locks and related
* synchronizers (semaphores, events, etc) that rely on
* first-in-first-out (FIFO) wait queues. This class is designed to
* be a useful basis for most kinds of synchronizers that rely on a
* single atomic {@code int} value to represent state. Subclasses
* must define the protected methods that change this state, and which
* define what that state means in terms of this object being acquired
* or released. Given these, the other methods in this class carry
* out all queuing and blocking mechanics. Subclasses can maintain
* other state fields, but only the atomically updated {@code int}
* value manipulated using methods {@link #getState}, {@link
* #setState} and {@link #compareAndSetState} is tracked with respect
* to synchronization.
aqs结构
以下是aqs的state属性,代表锁的状态。
FIFO队列的head、tail,代表竞争锁的线程队列的头尾节点
private transient volatile Node head; private transient volatile Node tail; private volatile int state;
其中Node类有以下几个属性
/** Marker to indicate a node is waiting in shared mode */ /**标记表示node等待在共享模式*/ static final Node SHARED = new Node(); /** Marker to indicate a node is waiting in exclusive mode */ /**标记用来表示节点是独占模式*/ static final Node EXCLUSIVE = null; /** waitStatus value to indicate thread has cancelled */ /**等待状态值用来表示线程已经取消了*/ static final int CANCELLED = 1; /** waitStatus value to indicate successor's thread needs unparking */ /**等待状态用来表示继任者线程需要唤醒*/ static final int SIGNAL = -1; /** waitStatus value to indicate thread is waiting on condition */ /**等待状态表示线程等待在一个condition上*/ static final int CONDITION = -2; /** * waitStatus value to indicate the next acquireShared should * unconditionally propagate */ /**等待状态表示下一个acquireShared方法需要无条件的传播*/ static final int PROPAGATE = -3; volatile Node prev;//node在队列中的前一节点 volatile Node next;//node在队列中的后一节点 volatile Thread thread;//持有node的线程引用
aqs方法
aqs最主要的几个方法如下:
独占锁
public final void acquire(int arg)//获取独占锁方法public final void acquireInterruptibly(int arg)//获取独占锁的可中断版本public final boolean tryAcquireNanos(int arg, long nanosTimeout)//获取独占锁的带超时版本//尝试获取独占锁的方法,交由子类实现,形成各种不同的获取锁策略protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException();}//独占锁释放方法public final boolean release(int arg)//尝试释放独占锁的方法,交由子类实现,形成各种不同的释放锁策略protected boolean tryRelease(int arg) { throw new UnsupportedOperationException();}
共享锁
public final void acquireShared(int arg)//获取共享锁方法public final void acquireSharedInterruptibly(int arg)//获取共享锁的可中断版本private boolean doAcquireSharedNanos(int arg, long nanosTimeout)//获取共享锁的带超时版本//尝试获取共享锁的方法,交由子类实现,形成各种不同的获取锁策略protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException();}//尝试释放独占锁的方法,交由子类实现,形成各种不同的释放锁策略public final boolean releaseShared(int arg) protected boolean tryReleaseShared(int arg) { throw new UnsupportedOperationException();}
原理讲解
acquire步骤
aqs定义了获取锁,释放锁的流程,但是对具体如何获取一个锁,释放一个锁则交由子类来实现。我们一起来看看aqs获取独占锁的流程:
public final void acquire(int arg) { /** * 1 tryAcquire尝试获取独占锁,成功则获取成功,方法完成,不成功则进入步骤2 * 2 addWaiter 创建一个独占模式node,添加到锁竞争线程队列并进入到步骤3 * 3 acquireQueued 竞争线程队列中节点尝试获取锁 */ if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
acquire步骤1
由子类实现
acquire步骤2
获取锁失败,添加一个等待节点加入到竞争队列
private Node addWaiter(Node mode) { //1.新建一个代表当前线程的node Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; //2.如果等待队列尾节点不为空,则将node加入到队列尾部 if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //如果队列尾为空,则将node加入到队列头 enq(node); return node; }
acquire步骤3:
加入到竞争队列中节点尝试获取锁
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { //1.获取当前节点的前一节点 final Node p = node.predecessor(); //2.如果前一节点是头节点,则尝试获取锁(公平锁的获取方式,只有队列的第二 个节点才会尝试获取锁) if (p == head && tryAcquire(arg)) { //获取锁则将当前节点设置为头结点 setHead(node); p.next = null; // help GC failed = false; return interrupted; } //3. 获取锁失败后判断是否需要阻塞,如果是进入4 //4 阻塞当前线程,如果阻塞后被唤醒状态是已中断,设置为已中断 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
acquireQueued步骤3:
如果前一节点为SIGNAL状态,表示这个节点释放锁的时候会唤醒后续节点,则这个节点可以阻塞
acquireQueued步骤4
private final boolean parkAndCheckInterrupt() { LockSupport.park(this);//阻塞当前线程 return Thread.interrupted();//被唤醒后返回是否被中断}
release步骤
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; //如果头节点不为空,并且状态不为0,唤醒后续节点 if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false;}
acquireShared 步骤
public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) //尝试获取共享锁失败,则进入doAcquireShared方法 doAcquireShared(arg);}
doAcquireShared步骤
doAcquireShared与acquireQueued类似,但是会在获取了共享锁之后,会尝试唤醒后续的节点,让其来获取共享锁
private void doAcquireShared(int arg) { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { boolean interrupted = false; for (;;) { //如果新增的节点前任节点是头节点 final Node p = node.predecessor(); if (p == head) { //尝试获取共享锁 int r = tryAcquireShared(arg); if (r >= 0) { //获取共享锁成功则设置自己为头节点并尝试唤醒后续节点 setHeadAndPropagate(node, r); p.next = null; // help GC 回收之前的头节点 if (interrupted) selfInterrupt(); failed = false; return; } } //如果获取锁失败,前任节点状态为SIGNAL,则当前节点阻塞 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
- java aqs原理浅析
- AQS的原理浅析
- AQS的原理浅析
- AQS的原理浅析
- AQS原理浅析
- AQS的原理浅析
- java多线程系列--AQS-01之独占锁原理浅析
- 书籍中的一个小样章-Java并发编程AQS原理浅析
- Java并发编程--AQS原理介绍
- JAVA多线程系列--ReentrantLock实现原理-AQS详解
- AQS锁机制原理
- AQS原理与源码
- 多线程之AQS原理
- AQS原理剖析
- AQS原理解析
- JAVA EJB原理浅析
- java虚拟机原理浅析
- java编译原理浅析
- android Camera 数据流程分析
- 常见浏览器兼容性问题与解决方案
- *统计字符串中每个字符出现的个数
- Lua面向对象
- Java集合框架List,Map,Set等全面介绍
- java aqs原理浅析
- 集合去重
- 自己写的框架的问题
- android native service编写及两个服务进程通讯
- Apache commons-io LineIterator学习
- 跨进程C/S native service服务编写
- MapReduce框架在Yarn上的详解
- 如何自己编写Arduino支持的C++类库
- Android的Camera架构介绍