5.抽象队列同步器AQS引用Lock详解
0.前序
说起并发锁Lock,我们肯定就想起了Doug Lea,[美国国籍,现担任[纽约州立大学]Oswego分校教师 。Doug Lea 在Java发展过程中扮演了举足轻重的角色, Tiger广纳了15项JSRs(Java Specification Requests)的语法及标准,其中一项便是JSR-166。JSR-166是来自于Doug编写的util.concurrent包。
Java并发编程核心在于iava.util.concurrent包而juc当中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等,而这个行为的抽象就是基于AbstractQueuedSynchronizer简称AQS,AQS定义了一套多线程访问共享资源的同步器框架,是一个依赖状态(state)的同步器。
0.1Lock总体介绍
从上面的图可知ReentrantLock是基于AQS实现的,具有可重入和公平性的特点;我们思考下用伪代码写下Lock锁的实现:
//多个争取锁,一个成功其他等待,需要一个无尽的循环--自旋
for(;;){ //while(true)
if(获取锁){//CAS算法加锁
break;//跳出循环
}
Thread.yeild();//让出CPU使用权其他的线程或Thread.sleep();
//
HeadSet,LinkedQueued();
put(Thread);
LockSupport.park();//阻塞
}
大致的逻辑是:必须有一个循环一直的可以让多个线程获取锁,一个获取成功,其他的线程阻塞等待,直到那个释放锁;
0.2CAS算法知识扩展
CAS是一个算法逻辑:例如两个线程同时更新一个变量,两个线程的工作内存中都有一个变量的副本,当要修改变量值时,会先和主内存中的变量做比较如果副本中的值和主内存中的值一样,就更新,如果不一样重新获取变量值,在更新;先比较再交换,两个操作组成的原子操作。
总结:LOCK 核心功能:自旋(循环),cas算法加锁,LocksSouport(阻塞和唤醒) ,队列(公平锁和非公平锁)
1.ReentrantLock
ReentrantLock是一种基干AQS框架的应用实现,是JDK中的一种线程并发访问的同步手段,它的功能类似于synchronized是一种互斥锁,可以保证线程安全,而目它具有比synchronized更多的特性,比如它支持手动加锁与解锁,支持加锁的公平性。
//使用ReentrantLock进行同步
ReentrantLock lock =new ReentrantLock(false);//false为非公平锁,true为公平锁
lock.lock()//加锁
lock.unlock()//解锁s
ReentrantLock如何实现synchronized不具备的公平与非公平性呢?
在ReentrantLock内部定义了一个Sync的内部类,该类继承AbstractQueuedSynchronized,对该抽象类的部分方法做了实现;并且还定义了两个子类
1)FairSync 公平锁的实现
2)NonfairSync非公平锁的实现
这两个类都继承自Sync,也就是间接继承了AbstractQueuedSynchronized,所以这一个ReentrantLock同时具备公平与非公平特性。上面主要涉及的设计模式:模板模式-子类根据需要做具体业务实现 AOS具备特性
2.AQS具备特性
- 阻塞等待队列
- 共享/独占
- 公平/非公平
- 可重入
- 允许中断
除了Lock外,Java.util.concurrent当中同步器的实现如LatchBarrier,BlockingQueue等,都是基于AQS框架实现
-
一般通过定义内部类Sync继承AQS
-
将同步器所有调用都映射到Sync对应的方法
AQS内部维护属性volatile int state(32位)
- state表示资源的可用状态
State三种访问方式:getState()、setState()、compareAndSetState()
AQS定义两种资源共享方式
1)Exclusive-独占,只有一个线程能执行,如ReentrantLock
2)Share-共享,多个线程可以同时执行,如Semaphore/CountDownLatch
2.1AQS定义两种队列
- 同步等待队列
- 条件等待队列
不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。
自定义同步器实现时主要实现以下几种方法:。
-
isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它
-
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
-
tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
-
tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源
-
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false
2.1.1同步等待队列
AQS当中的同步等待队列也称CLH队列,CLH队列是Craig、Landin、Hagersten三人发明的一种基于双向链表数据结构的队列,是FIFO先入先出线程等待队列,Java中的CLH队列是原CLH队列的一个变种,线程由原自旋机制改为阻塞机制。