【深入理解并发编程系列5】抽象队列同步器AQS Lock详解

背景

为了提升锁性能引入了 锁粗化技术。

锁粗化:
多个同步都加统一个对象。反复不停进出一个对象。JVM会对这样的代码进行逃逸分析。对整个代码块进行扫描。如果符合这种特征代码的话,会将多个同步块合并成一个大的同步快,变粗了。

锁消除:
如果对象不可能被多个对象访问到。通过逃逸分析,会对锁进行消除。比如只加在对象上的锁。对象的生命周期是线程调用周期,对象只被一个线程访问。此时会对锁进行消除。

还有一个典型的就是栈上分配的对象,符合逃逸分析的对象,分配在栈上。并且对这个对象进行了加锁。此时,也只有这个线程能对对象进行访问,就会进行锁消除。

PS:逃逸分析后会做的优化:锁消除,锁粗化,标量替换等。
标量:八大基本类型。

AQS LOCK详解

  1. AQS还没出现之前,如果需要规定一个线程占用CPU使用时间的话,可能用到while循环。

或者用:lock.pack(); lock.Unpack(THread) 停止,阻塞方法。

unpack

CAS

Lock三大核心:自旋,LockSupport,CAS,队列

队列的FIFO用来实现公平,非公平锁。

CAS依赖硬件元语:cmpxchg()

LockSupport:线程阻塞工具。底层是native的方法 UNSAFE.xx

AQS具备特性:

  • 阻塞等待队列
  • 共享/独占
  • 公平/非公平
  • 可重入
  • 允许中断

除了Lock外,Java.concurrent.util当中同步器的实现如Latch,Barrier,BlockingQueue等, 都是基于AQS框架实现

AQS内部维护属性volatile int state (32位)

state表示资源可用状态。

  • state三种访问方式:
  1. getState()
  2. setState(int)
  3. compareAndSetState()
  • AQS定义两种资源共享方式
  1. Exclusive-独占,只有一个线程能执行,如ReentrantLock。
  2. Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch。
  3. 同步等待队列
  4. 同步等待队列
  • 不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共 享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/ 唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:
  1. isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去 实现它。
  2. tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回 false。
  3. tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回 false。
  4. tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成 功,但没有剩余可用资源;正数表示成功,且有剩余资源。