当前位置: 首页 > 后端技术 > Java

看完这篇文章,Lock和AQS是兄弟

时间:2023-04-01 20:59:49 Java

大家好,我是奇喵小屋,一个分享技术和生活的博主。以下是我的主页。各首页同步更新优质博客。创建起来并不容易。还请点开掘金主页了解主页Segmentfault主页开源中国更多MySQL、Redis、并发、JVM、分布式等面试热点知识稍后会在主页发布,还有Java学习路线、面试重点、职业规划、面经等相关博文转载请注明出处!0、Lock与AQS类图1、Lock接口1.1Lock的定义Lock接口定义了锁的API操作,用于实现java中的锁机制publicinterfaceLock{//如果锁可用,获取后返回thelock//如果锁不可用,则当前线程会被阻塞,直到获取到锁后才会返回。空锁();//可中断获取锁(获取锁的过程可以被中断)//如果锁可用,获取锁后返回//如果锁不可用,线程将阻塞获取锁。阻塞获取锁的过程可以被打断,被打断会抛出InterruptedExceptionvoidlockInterruptibly()throwsInterruptedException;//非阻塞尝试获取锁//如果锁可用,获取锁后,返回true//如果锁不可用,返回falsebooleantryLock();//尝试获取锁(获取锁的过程可以中断)//如果锁可用,获取锁后,返回true//如果锁不可用,则继续尝试获取指定时间内的锁//如果成功获取锁-returntrue//如果被中断-throwInterruptedException//如果超时-returnfalsebooleantryLock(longtime,TimeUnitunit)throwsInterruptedException;//释放锁voidunlock();//获取Condition对象,Condition对象与当前Lock对象绑定,当前线程只有在获取到锁后才能调用Condition对象的await()方法,调用后,当前线程的锁就会被释放并进入Conditon对象WAT的等待队列ING//当其他线程调用Condition对象的signal()时,Condition对象等待队列中的WATING线程会被唤醒ConditionnewCondition();}1.2Lock和sychronized的异同有可重入锁Lock的子类的区别在于Lock显式获取锁和释放锁。Sychronized隐式获取锁并释放锁。Lock只能锁定代码块。sychronized可以锁定代码块。加锁方法依赖JDK实现sychronized,依赖JVM实现Lock。独占模式和共享模式,每种模式又分为公平锁和非公平锁。同步是一种独占模式。不公平锁锁是可中断的。线程在获取锁的过程中会影响中断。同步不能中断。线程被阻塞等待锁的释放。有时,不会响应中断的Lock可以设置超时时间,超时后返回sychronized3.一个锁实例可以有多个Condition对象//相当于monitor对象的WaitSet//必须持有在调用其方法之前绑定到Condition对象的锁publicinterfaceCondition{//相当于Object.wait(),Interruptible//持有锁的线程释放锁,进入等待队列,线程状态变为WATING,直到以下两种情况唤醒//1。其他线程调用等待队列//2的signal()或signalAll()。被其他线程中断//唤醒后,线程会从等待队列移动到锁实例的同步队列,状态会从WAITING变为BLOKINGvoidawait()throwsInterruptedException;//与await()相同,但不可中断voidawaitUninterruptibly();//同await(),但多了一种唤醒情况——等待超时(单位纳秒)longawaitNanos(longnanosTimeout)throwsInterruptedException;//与await()相同,但更多有一种唤醒情况——等待超时booleanawait(longtime,TimeUnitunit)throwsInterruptedException;//与await()相同,但有一个额外的唤醒情况-等待超时booleanawaitUntil(Datedeadline)throwsInterruptedException;//将等待队列中的第一个节点移动到锁实例的同步队列中,然后通过LockSupportvoidsignal()唤醒线程;//将等待队列中的所有节点移动到锁实例的同步队列中,然后通过LockSupportvoidsignalAll();唤醒线程}2.2Condition和monitorCondition的区别——在等待队列中等待的线程是一个中断响应监视器——等待队列中等待的线程不能响应中断条件——等待队列中的线程可以指定等待到未来3.AQS3.1AQS引入AbstractQueuedSynchronizerAbstractQueueSynchronizer——基础framework子类用于构建锁或其他同步组件通过继承AQS并实现其抽象方法实现锁AQS支持两种模式——独占模式和共享模式3.2AQS结构AOS的核心字段——exclusiveOwnerThread在独占模式下,持有锁的线程AQS核心字段——statestate用于表示同步器的状态(可以称为同步State)AQS的不同子类使用state的方式不同。ForReentrantLockstate=0—表示同步器未被占用(没有线程持有锁)state!=0—表示同步器被占用(有线程正在使用锁)尝试获取锁——检查state的值是否为0,如果为0,尝试用CAS修改它的值,如果不为0——那么不获取锁,尝试释放锁——改变stateCAS的值为0对于CountDownLatch,CyclicBarrierstate有不同的状态ofusingNode-waitStatusCANCELLED(1):同步队列中的节点被中断或超时INITIAL(0):初始化状态SIGNAL(-1):CONDITION(-2):表示节点在等待队列中PROPAGATE(-3):AQS有5个子类锁实现独占模式的方法booleantryAcquire()booleantryRelease()booleanIsHeldExclusively()共享模式booleantryAcquireShared()booleantryReleaseShared()3.3AQS独占模式AQS独占模式下同步队列的第一个节点持有Lock如果线程获取锁失败,会被封装成一个Node,然后加入到同步队列中,开始自旋。从同步队列中移除节点的条件——当前驱节点为首节点且成功获取锁节点并释放锁时——将唤醒其后继节点3.3.1获取同步队列中的锁,只有当节点的前驱节点是第一个节点时,才能尝试获取锁。原因如下。第一个节点是获取锁的节点。第一个节点释放锁后,会唤醒它的后继节点,后继节点被唤醒后,需要检查它的前驱节点是否是第一个节点,以保持同步队列的先进先出原则。3.3.2解除锁定。在需要阻塞之前,它会先判断是否超时。如果超时,它将返回false。如果需要阻塞,还会检查剩余时间是否大于阈值。如果大于阈值——使用LockSupport让线程超时并阻塞LockSupport.parkNanos(this,nanosTimeout);3.4AQS共享模式3.4.1共享模式和独占模式的区别共享模式和独占模式的区别在于多个线程是否可以同时获取锁允许访问,不允许独占访问独占模式访问资源-所有其他访问都不允许,阻塞:任何线程获得锁后,可以再次获得锁而不会被锁阻塞。ReentrantLock实现了AQS的独占模式。是一种可重入锁,分为公平锁和非公平锁。公平锁:首先请求锁非公平锁比公平锁更高效。不公平的锁可能会导致线程饥饿——某些线程长时间无法获取资源。ReentrantLock的大部分方法都是由Sync及其子类实现的。,ReentrantLock只暴露接口4.2ReentrantLock获取锁4.2.1非公平锁4.2.2公平锁4.2.3公平锁和非公平锁的区别FairSync和NonfairSync的lock()和tryAcquire()逻辑是不同的。在lock()方法的开始,非公平锁会尝试通过CAS修改同步状态来获取锁。公平锁不会自旋。非公平锁和公平锁都会在前驱节点为同步队列首节点时调用tryAcquire()尝试获取锁。在tryAcquire()中,如果状态为0,非公平锁不会关心节点在同步队列中的位置,直接尝试CAS修改状态获取锁;但是非公平锁关心节点的位置,会检查是否有前驱节点,如果有则放弃以上两点,保证公平锁一定是——先获取锁的线程一定要先获取锁,非公平锁不一定4.3ReentrantLock释放锁公平锁释放锁和非公平锁释放锁使用相同的逻辑