大家好,这里是淇妙小屋,一个分享技术,分享生存的博主
以下是我的主页,各个主页同步更新优质博客,创作不易,还请大家点波关注
掘金主页
知乎主页
Segmentfault主页
开源中国主页
后续会公布更多MySQL,Redis,并发,JVM,分布式等面试热点常识,以及Java学习路线,面试重点,职业规划,面经等相干博客
转载请表明出处!
0. Lock与AQS类图
1. Lock接口
1.1 Lock的定义
Lock接口定义了锁的API操作,用于实现java中锁机制
public interface Lock { //如果锁可用,则取得锁后返回 //如果锁不可用,那么以后线程会阻塞,直到获取锁后才会返回 void lock(); //可中断的获取锁(取得锁的过程可中断) //如果锁可用,则取得锁后返回 //如果锁不可用,那么线程会阻塞获取锁,阻塞获取锁的过程是可中断的,受到中断会抛出InterruptedException void lockInterruptibly() throws InterruptedException; //非阻塞的尝试取得锁 //如果锁可用,取得锁后,返回true //如果锁不可用,返回false boolean tryLock(); //尝试获取锁(取得锁的过程可中断) //如果锁可用,取得锁后,返回true //如果锁不可用,那么在指定工夫内会一直的尝试取得锁 //如果胜利取得锁——返回true //如果被中断——抛出InterruptedException //如果超时——返回false boolean tryLock(long time, TimeUnit unit) throws InterruptedException; //开释锁 void unlock(); //获取Condition对象,Condition对象与以后Lock对象绑定,以后线程只有取得了锁 //能力调用该Condition对象的await()办法,而调用后,以后线程将开释锁,进入Conditon对象的期待队列中WATING //当其余线程调用Condition对象的signal(),才会唤醒Condition对象的期待队列中的WATING线程 Condition newCondition();}
1.2 Lock与sychronized的异同
共同点
- Lock的子类中有可重入锁
区别
Lock显式的获取锁与开释锁
sychronized隐式的获取锁与开释锁
Lock只能给代码块上锁
sychronized能够给代码块,办法上锁
Lock依赖JDK实现
sychronized依赖JVM实现
Lock有独占模式与共享模式,每个模式还分偏心锁与非偏心锁
sychronized是独占模式的非偏心锁
Lock是可中断的,线程在取得锁的过程中是能够影响中断
sychronized不可中断,线程在阻塞期待锁的开释的时候,是不会响应中断的
Lock能够设定超时工夫,超时会返回
sychronized不行
2. Condition接口
2.1 Condition接口定义
//Condition对象——1.相当于一个期待队列 2.必须与一个锁实例绑定 3.一个锁实例能够有多个Condition对象//相当于monitor对象的WaitSet//必须持有Condition对象绑定的锁,才能够调用其办法public interface Condition { //相当于Object.wait(),可中断 //持有锁的线程开释锁,进入期待队列中,线程状态更变为WATING,直到以下2种状况醒来 //1.有其余线程调用期待队列的signal()或signalAll() //2.被其余线程中断 //醒来后的线程会从期待队列挪动到锁实例的同步队列,状态由WAITING更改为BLOKING void await() throws InterruptedException; //同await(),然而不可中断 void awaitUninterruptibly(); //同 await(),然而多了一种唤醒状况——期待超时(单位纳秒) long awaitNanos(long nanosTimeout) throws InterruptedException; //同 await(),然而多了一种唤醒状况——期待超时 boolean await(long time, TimeUnit unit) throws InterruptedException; //同 await(),然而多了一种唤醒状况——期待超时 boolean awaitUntil(Date deadline) throws InterruptedException; //将期待队列中的首节点挪动到锁实例的同步队列,而后通过LockSupport唤醒线程 void signal(); //将期待队列中的全副节点挪动到锁实例的同步队列,而后通过LockSupport唤醒线程 void signalAll();}
2.2 Condition与monitor的区别
Condition——期待队列中期待的线程是可响应中断的
monitor——期待队列中期待的线程不可响应中断
Condition————期待队列中的线程能够指定期待到将来的某个具体工夫点
monitor——不反对
3. AQS
3.1 AQS介绍
AbstractQueuedSynchronizer形象队列同步器——用于构建锁或其余同步组件的根底框架
子类通过继承AQS并实现它的形象办法来实现锁
AQS反对两种模式——独占模式,共享模式
3.2 AQS构造
AOS的外围字段——exclusiveOwnerThread
- 独占模式下,持有锁的线程
AQS最外围字段——state
- state用于示意同步器的状态(可称为同步状态)
AQS的不同子类对state的使用不同
对于ReentrantLock
state=0——示意同步器没有被占用(没有线程持有锁)
state!=0——示意同步器已被占用(有线程正在应用锁)
尝试获取锁——查看state的值是否为0,如果为0尝试用CAS批改其值,如果不为0—则未获得锁
尝试开释锁——将state的值CAS批改为0
对于CountDownLatch,CyclicBarrier
- state有不同的使用
Node的状态——waitStatus
- CANCELLED(1):同步队列中的节点被中断或者超时
- INITIAL(0):初始化状态
- SIGNAL(-1):
- CONDITION(-2):示意节点在期待队列中
- PROPAGATE(-3):
AQS有5个办法供子类锁去实现
独占模式
- boolean tryAcquire()
- boolean tryRelease()
- boolean IsHeldExclusively()
共享模式
- boolean tryAcquireShared()
- boolean tryReleaseShared()
3.3 AQS独占模式
AQS独占模式下
- 同步队列的首节点持有锁
- 如果线程尝试获取锁失败,那么会封装成一个Node,而后退出同步队列,并开始自旋
- 节点从同步队列移除的条件——前继节点为首节点 and 胜利取得锁
- 节点开释锁时——会唤醒其后继节点
3.3.1 获取锁
同步队列中,只有当节点的前继节点是首节点,能力尝试获得锁,起因如下
首节点是获得锁的节点,首节点开释锁后,会唤醒其后继节点
后继节点被唤醒后须要查看本人的前继节点是否为首节点
- 保护同步队列的FIFO准则
3.3.2 开释锁
3.3.3 超时获取锁
过程根本与获取锁雷同
区别在于
- 每次自旋操作,在判断本人是否须要被阻塞之前,会优先判断是否已超时,如果超时了,就返回false
如果须要被阻塞,还会查看残余的工夫有没有大于阈值
如果大于阈值——通过LockSupport让线程进行超时阻塞
LockSupport.parkNanos(this, nanosTimeout);
3.4 AQS共享模式
3.4.1 共享模式与独占模式的区别
共享模式与独占模式的区别在于——同一时刻是否有多个线程能够同时获取到锁
- 共享模式拜访资源——其余共享式拜访容许,独占式拜访不容许
- 独占模式拜访资源——其余拜访一律不容许,被阻塞
3.4.2 共享模式取得锁
3.4.3 共享模式开释锁
4. ReentrantLock实现AQS的独占模式
4.1 ReentrantLock介绍
可重入:任意线程取得锁后可能再次获取该锁而不会被锁阻塞
ReentrantLock实现了AQS的独占模式,是一个可重入锁,还分为 偏心锁与 非偏心锁
- 偏心锁:先对锁进行获取申请的线程肯定先取得锁
- 非偏心锁
非偏心锁的效率高于偏心锁
非偏心锁可能呈现 线程饥饿问题——局部线程迟迟无奈取得资源
ReentrantLock大多数办法的实现都是Sync及其子类来实现,ReentrantLock只是对外裸露了接口
4.2 ReentrantLock取得锁
4.2.1 非偏心锁
4.2.2 偏心锁
4.2.3 偏心锁与非偏心锁的不同
FairSync和NonfairSync的 lock() 和 tryAcquire() 逻辑不同
- 非偏心锁在lock()办法的开始就会尝试去通过CAS批改同步状态以取得锁,偏心锁不会
在自旋时,非偏心锁和偏心锁都会在前继节点为同步队列首节点时,调用tryAcquire()尝试获取锁
在 tryAcquire()中,如果state为0,那么非偏心锁不会关怀节点在同步队列中的地位,间接尝试CAS批改state取得锁;然而非偏心锁关怀节点的地位,会查看是否有前继节点,如果有,就会放弃
上述2点保障了偏心锁肯定是——先对锁进行获取申请的线程肯定先取得锁,而非偏心锁不肯定
4.3 ReentrantLock开释锁
偏心锁开释锁与非偏心锁开释锁采纳同一个逻辑