并发编程AQS源码分析(并发编程模型)

  本篇文章为你整理了并发编程AQS源码分析(并发编程模型)的详细内容,包含有并发编程案例 并发编程模型 并发编程使用场景 并发编程实战pdf 并发编程AQS源码分析,希望能帮助你了解 并发编程AQS源码分析。

  并发编程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分为公平锁和非公平锁、默认是非公平锁

  公平锁:按照队列先进先出的顺序进行加锁解锁

  非公平锁:哪个线程先抢到锁就是哪个的、有可能造成某一个线程一直抢不到锁

  

// 非公平锁

 

  final void lock() {

   // 进行cas操作、state值为0则赋值为1、成功获取锁

   if (compareAndSetState(0, 1))

   // 设置线程标记、线程标记用来检测是否是重入锁

   setExclusiveOwnerThread(Thread.currentThread());

   else

   // 调用AQS中的acquire方法、在调用ReentrantLock类下定义的Sync类中的nonfairTryAcquire方法进行具体的锁操作细节

   // 传参1、里面一直进行for循环CAS赋值、直到哪个线程抢到返回

   acquire(1);

  // acquire方法是操作state状态位的方法、通过tryAcquire里面的CAS原子操作检测锁状态

  public final void acquire(int arg) {

   // tryAcquire调用nonfairTryAcquire方法

   if (!tryAcquire(arg)

   // addWaiter: 根据给定模式创建一个当前线程的Node节点并返回、当前是创建一个独占锁的Node节点

   acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

   // 中断当前线程

   selfInterrupt();

  final boolean nonfairTryAcquire(int acquires) {

   // 先获取当前线程标识符、用于检测当前对象的线程标识符是否是同一个、同一个则为可重入锁、可直接返回、不是则返回后调用acquireQueued

   final Thread current = Thread.currentThread();

   // 获取计数器、表明当前线程重入了多少次锁

   int c = getState();

   // 为0则代表当前线程的锁全部解锁完毕

   if (c == 0) {

   // 给state计数器加1、代表上锁

   if (compareAndSetState(0, acquires)) {

   // 设置对象的线程标识符为当前线程

   setExclusiveOwnerThread(current);

   // 直接返回

   return true;

   // 代表当前有线程在使用该对象锁、检测是否是同一线程、是则进入、为可重入锁

   else if (current == getExclusiveOwnerThread()) {

   // 计算state

   int nextc = c + acquires;

   // 小于0则代表锁过多、即0x7fffffff + 1为负数

   if (nextc 0) // overflow

   throw new Error("Maximum lock count exceeded");

   // 赋值state

   setState(nextc);

   // 直接返回

   return true;

   // 返回false、表示当前对象锁被其他线程占用了、需要等到加锁的线程解锁才进行抢占

   return false;

  // 根据给定模式创建一个当前线程的Node节点并返回

  private Node addWaiter(Node mode) {

   // 根据当前线程创建一个Node节点、并传入模式(独占锁、共享锁)

   Node node = new Node(Thread.currentThread(), mode);

   // 获取队列尾部Node节点

   Node pred = tail;

   // 检测队列是否存在尾部节点、即是否存在节点

   if (pred != null) {

   // 新创建的Node节点上一个节点设为队列的尾部节点、然后下面直接将新创建节点插入队列尾部

   node.prev = pred;

   // 通过cas操作更换节点顺序、即新创建的节点为尾部、原先尾节点的下一个节点设置为当前新创建的Node节点

   if (compareAndSetTail(pred, node)) {

   pred.next = node;

   return node;

   // 将新创建的Node节点插入队列尾部

   enq(node);

   return node;

  // 里面for死循环无限调用nonfairTryAcquire方法检测锁是否被释放、然后进行抢占加锁

  final boolean acquireQueued(final Node node, int arg) {

   // 错误位、当finally执行时该变量为true、则代表有异常发生、取消当前的操作

   boolean failed = true;

   try {

   boolean interrupted = false;

   for (;;) {

   // 返回Node节点的上一个节点

   final Node p = node.predecessor();

   // 检测是否是头节点、是的话在进行cas检测锁是否解锁在抢占

   if (p == head tryAcquire(arg)) {

   // 抢占到锁了、设置当前线程的Node节点为等待队列的头节点

   setHead(node);

   p.next = null; // help GC

   failed = false;

   return interrupted;

   if (shouldParkAfterFailedAcquire(p, node)

   // 检测当前线程是否调用了interrupt中断线程方法、并且检测状态位进行阻塞、调用LockSupport.park(this)方式阻塞自己

   parkAndCheckInterrupt())

   // 如果循环走到这里在return返回的话、代表线程将中断

   interrupted = true;

   } finally {

   if (failed)

   cancelAcquire(node);

  

 

  以上就是并发编程AQS源码分析(并发编程模型)的详细内容,想要了解更多 并发编程AQS源码分析的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: