当一个线程请求一个共享资源时,如果资源处于空闲状态,则设置该线程为有效的工作线程,并设置该资源为锁定状态;当资源处于锁定状态时,需要设置其他请求的线程处于阻塞状态,并在共享资源被唤醒时,分配当前处于阻塞状态的线程使其工作。
使用一个FIFO的双端队列,用于存放锁上阻塞状态的线程,同时使用voliatile int state表示当前锁的状态,使用Unsafe工具对原子性操作来对state进行修改。
即当一个线程尝试获取锁时,如果锁已经被占用,则将该线程以及等待状态等信息构造为一个Node节点,加入到同步队列的尾部,同时阻塞当前线程。当队列顶部的线程节点,在占用资源完成后,释放资源,同时唤醒后面的节点并释放当前节点的引用。
AQS,抽象同步队列。基于模板模式的设计,即其实现类通过重写指定的方法,同时调用父类特定的方法,从而达到调用子类重写的方法的目的。
-
- Jdk方法
- volatile int state 相关:unsafe使用CAS协议实现
- Jdk方法
- getState() 返回同步状态的当前值
- setState(int newState) 设置同步状态的值
- compareAndSetState(int expect, int update)
使用CAS设置当前状态;如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。
通过懒加载生成的,处于等待锁的队列的头、尾节点。
除了初始化,该头节点只能通过setHead()方法进行修改。
如果头节点存在,则头节点的waitStatus 属性不能被设置为CANCELLED状态(线程已取消)
通过懒加载方式生成的,处于等待锁的队列的尾节点;只支持add方式的编辑。
AQS仅仅只是提供独占锁和共享锁两种方式,但是每种方式都有响应中断和不响应中断的区别。
其内部的方法均为空的实现,由子类实现,而子类通常被称为自定义同步装置的内部类。
- 不响应中断的独占锁:acquire(int arg)
- 响应中断的独占锁:acquireInterruptibly(int arg)
- 不响应中断的共享锁:acquireShared(int arg)
- 响应中断的共享锁:acquireSharedInterruptibly(int arg)
当前同步器是否是在独占模式下的调用
- volatile int waitStatus:节点的状态
CANCELLED:1,代表线程已经取消。
当线程等待超时或者被中断时,需要从之前的等待状态设置为取消等待状态;
SIGNAL:-1,该线程后面的线程需要启动。
当前节点释放同步状态或者被取消时,需要通知给后继节点;
CONDITION:-2,线程正在等待condition。
该节点当前存在在等待队列Condition上,当其他线程对Condition调用signal()方法后,需要将该节点共等待队列转移到同步队列中,以获取同步状态。
PROPAGATE,值为-3,下一次的共享状态会被无条件的传播下去;
INITIAL:0,初始状态,节点在同步队列中等待获取锁。
- Node prev:前驱节点 Node next:后继节点
- Node nextWaiter:下一个等待condition的node
- Thread thread:获取同步状态的线程
- Node SHARED = new Node():当前是共享节点; Node EXCLUSIVE = null:独占节点
AbstractQueuedSynchronizer是一个抽象类,在多线程体系中是底层的实现;其子类(锁),通过继承的方式,重写AQS内部的方法(置空方法),从而实现自身的业务。
AQS全称AbstractQueuedSynchronizer,java.util.concurrent.locks包中一个抽象类,含一个用于存储被阻塞的线程的队列,以及一个表示对应线程等待状态的waitStatus值。
当线程t调用自定义的Lock.lock()方法时,主要执行以下逻辑:
- AQS.acqurie()方法,调用子类同步器重写的tryacquire()方法,尝试获取锁,并且成功;
设置当前线程为AOS的工作线程,并设置AQS同步器的state为占用状态。
- 获取锁失败后,通过acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法,添加该线程节点到FIFO队列中;并对FIFO队列清除非法节点,完成后阻塞当前线程,LockSupport.park(this);
- 当其他线程通过release方法释放锁,并唤醒该线程t后,线程t启动,并尝试获取锁,直至获取成功;
- 设置当前线程t所在的节点为队列的head节点
当线程t2执行完成后,调用自定义锁的Lock.unlock()方法时,主要执行以下逻辑:
- 调用子类重写的AQS方法Lock.Sync.tryRelease(1),用于设置AQS的state为0,设置AOS的工作线程为null;
- 在head.waitStatus != 0的情况下(即存在节点在队列中尝试获取锁),从尾节点开始便利获取靠近头节点最近的合法节点s(s.waitStatus <0);
- 唤醒节点s的线程LockSupport.unpark(s.thread);