序言
好久没有写文章了,最近太懒了哈哈,今天督促自己写一下AQS的学习笔记吧,因为前段时间刚温习了一遍。
AQS
AQS全称是:AbstractQueuedSynchronizer(抽象的队列同步器),它隶属于我们的JUC包,可以说AQS就是并发类的基类/规范了,很多类都是基于AQS实现的:lock锁,CountDownLatch,CyclicBarrier,Semaphore。
AQS的工作原理:当有线程来获取共享资源时,如果共享资源处于空闲状态,那么该线程就设置为工作线程,共享资源设为锁定状态,如果线程来获取锁,共享资源处于锁定状态,则将当前线程放入队列中并将其阻塞,直到共享资源变成空闲状态,则将其唤醒抢占资源。
AQS中的核心组件:
- state:一个被volatile修饰的int变量,0表示当前资源处于空闲状态,1表示当前资源处于锁定状态
- Node:它是用来封装线程的,还包括了指向前一个结点和指向后一个结点的指针,还有一个waitStatus字段用于保存当前Node结点等待的状态,Node还包括两个模式,Shared模式表以共享状态等待锁,EXCLUSIVE模式表以独占状态等待锁。
- FIFO队列:FIFO基于CLH虚拟的双端队列实现的,FIFO队列其实就是用Node结点以及头尾指针来具体实现。
AQS的具体实现:Lock
lock锁时基于AQS实现的,lock中有一个内部类Sync它继承了AQS类,Sync还有两个子类,一个FairLock公平锁和NoFairLock非公平锁,根据构造函数来创建公平锁和非公平锁,公平锁在获取资源时需要额外判断一个条件:当前队列为空或队列不为空,队列中头结点为当前线程。
lock的lock()方法:
- 线程来获取锁,判断state是否为0,如果为0则通过cas的方式获取锁,不为0或者cas获取失败则会调用acpuire()方法。
- acquire()方法:它会调用tryAcquire()方法和acquireQueued()方法
- tryAcquire()方法:继续尝试抢占锁,判断两个条件:当前state为0则尝试通过cas抢占锁,如果当前线程为已获取锁的线程,则可重入成功,否则获取锁失败
- acquireQueued():它会先调用一个addWaiter()方法,将当前线程封装成Node结点放入我们的FIFO队列当中,如果是第一次调用FIFO队列的话会先进行初始化(队列中的头结点是一个哨兵结点,后面的才是我们的Node结点),再在队列中尝试获取锁,如果失败的话就调用LockSupport.park()方法将线程阻塞掉。
- LockSupport:它也是用于阻塞/唤醒线程的类,但它不同于我们的wait,await/notify,signal方法,使用LockSupport阻塞/唤醒线程时并不需要像其他方法一样必须先获取锁,它可以在任意时刻来阻塞/唤醒线程。
lock的unlock()方法:
- 底层会调用tryRelease方法,将state设为0且将当前占用锁的线程设为null,然后将队列中处于阻塞状态的线程唤醒。
总结
生命不息,学习不止,与君共勉,希望我们都可以拥有美好的前程!