这一切都是命运石之门的选择!
1.AQS在jdk中的应用架构
打开idea,找到AbstractQueuedSynchronizer类,按住ctrl+alt+shift+u,显示如图:
然后选中AbstractQueuedSynchronizer,然后按ctrl+t,显示出aqs的应用:
然后全选,回车:
AQS在jdk的应用架构就是上面的结构。
2.AQS的特性
1:阻塞等待队列
2:共享/独占模式
3:支持公平/非公平加锁
4:可重入
5:可以中断
3.AQS的重要属性介绍
先来看AbstractQueuedSynchronizer的父类AbstractOwnableSynchronizer,打开AbstractOwnableSynchronizer,然后按alt+7:
黄色的为类的成员变量,红色的是方法;
exclusiveOwnerThread:独占模式下获取锁的线程;
我们接着看AQS的构成,一样的alt+7:
内部类Node:阻塞队列的实现,里面核心属性:(一个节点绑定一个线程,后续节点都代表一个线程)
SHARED:标记节点未共享模式,默认值为一个新的节点;
EXCLUSIVE:标记节点为独占模式,默认值为null;
CANCELLED:节点等待标记,如果waitStatus为这个值(1),表示线程已经被取消;(1,代表线程已经出现异常,可能是中断引起的异常,或者是程序本身的错误引起的,节点状态标记为1会被废弃掉)
SIGNAL:如果waitStatus为这个值(-1),通知后续节点继续运行标记;(-1,代表线程可以被唤醒)
CONDITION:如果waitStatus为这个值(-2),该节点会从等待队列中转移到同步队列中;(-2,条件等待)
PROPAGATE:如果waitStatus为这个值(-3),表示下一次共享式同步状态获取将会被无条件地传播下去;(-3,代表线程是可传播的)
waitStatus:被volatile修饰,变更以后别的线程可以及时可见,其中值为上面4个加上默认值0(1,0,‐1,‐2,‐3)的5种状态,使用CAS更改状态保证这个等待状态的原子性;
prev:前驱节点;
next:后置节点;
thread:节点绑定的线程;
nextWaiter:等待队列中的后继节点,如果当前节点是共享的,那么这个字段是一个SHARED常量,也就是说节点类型(独占和共享)和等待队列中的后继节点共用同一个字段。
Node方法:
isShared():判断节点是不是共享模式;
predecessor():返回前驱节点;
Node():默认构造函数;
Node(Thread thread, Node mode):绑定线程和下一个等待节点的构造函数;
Node(Thread thread, int waitStatus):绑定线程和等待状态的构造函数;
继续往下看:
内部类ConditionObject,节点等待条件类,暂时先跳过,方法也暂时先跳过,后面会讲到,继续往下;
重点属性来了:
head:同步队列的头节点;
tail:同步队列的尾节点;
state:加锁成功标记(具体由实现类来确定);
unsafe:cas的操作类;
stateOffset/headOffset/tailOffset/waitStatusOffset/nextOffset:对象属性的偏移量;(用于安全的访问对象属性,用于cas操作对象的指定字段)
4.AQS的同步实现原理
线程的自旋+cas操作加锁成功+队列:
lock(); while(true){//自旋操作 if (加锁成功,cas操作) { break;//结束自旋 } head.next = new Node(Thread.currentThread(),0);//加入到等待队列 LockSupport.park();//阻塞线程 } 业务代码; unlock();
Thread t = queued.take();//从队列中获取线程
LockSupport.unpark(t);//取消线程阻塞,让后续节点去竞争锁
吾王剑锋所指,我等心之所向!