J.U.C--locks--ReentrantLock ╰+攻爆jí腚メ 2022-07-11 13:14 183阅读 0赞 > 在JDK5之前对共享变量的访问只有synchronized和volatile。在JDK5.0之后增加了显示锁ReentrantLock(可重入锁、独占锁)。需要注意的是,ReentrantLock并不是用来替代synchronized的,而是作为一种高级主题,在内置加锁机制synchronized不适用时作为一个可选的高级主题。 # 1. Lock接口 # Lock是一个显示锁的接口,ReentrantLock就是Lock的一个实现类。Lock接口中定义了一组抽象的加锁操作。与内置的synchronized加锁操作不同,Lock提供: 1. 无条件的 2. 可轮询的; 3. 定时的; 4. 可中断的锁; Lock的所有加锁和解锁都是显式的。Lock的实现类必须提供与内部锁相同的内存可见性原语。但是可以在一下方面不同: 1. 加锁语义; 2. 调度算法; 3. 顺序保证; 4. 性能特性。 下面贴出Lock接口的函数: void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); # 2. ReentrantLock # 首先我们需要明确的一点是ReentrantLock的实现全部是基于同步器,也就是AQS这个类。ReentrantLock的所有核心方法都是通过AQS框架实现。 > ReentrantLock实现了Lock接口并提供了与synchronized相同的互斥性和内存可见性。在获取ReentrantLock锁时有着与进入同步代码块相同的内存语义;在释放ReentrantLock锁时有着与退出同步代码块相同的内存语义;此外还提供了可重入的加锁语义。 下面给出一个Lock的标准使用形式: Lock lock = new ReentrantLock(); ... lock.lock(); try{ //更新对象状态 //捕获异常 } finally{ lock.unlock(); } 切记,必须在finally块中释放掉Lock,否则,如果在try块中抛出了异常,这个锁就永远不能被释放了。 ## 1. ReentrantLock的属性 ## ReentrantLock类里面只有一个属性,也就是一个Sync变量,Sync这是一个内部类,继承于AbstractQueuedSynchronizer。 /** 同步器提供了所有的实现机制 */ private final Sync sync; ## 2. ReentrantLock的类继承图, ## ![这里写图片描述][SouthEast] ## 3. ReentrantLock的构造器 ## ReentrantLock有两个构造器,无参和带参的构造器; public ReentrantLock() { sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } 无参的构造器默认的是new一个非公平的锁。带参的构造器可以可以通过指定布尔类型的参数确定new公平的或则是不公平的锁。 这里的公平是指: 1. 公平的锁: 线程将按照它们发出的请求顺序来获得锁; 2. 非公平的锁: 允许”插队”,当一个线程请求非公平的锁时,如果在发出请求的同时,该锁的状态变成了可用,那么该线程将跳过队列中所有等待线程并获得锁。这样就避免了线程的切换。 ## 4. 内部类 ## 上面说了ReentrantLock是基于AQS实现的,在ReentrantLock内部中也只定义了一个属性:Sync的一个实例。其实Sync就是ReentrantLock内部继承于AQS的类。Sync又有两个实现子类:公平锁FairSync和非公平锁NonfairSync。其继承管理如下图: ![这里写图片描述][SouthEast 1] 从构造器我们也可以知道,ReentrantLock实际上默认使用的是NonfairSync,通过指定也可以使用FairSync。 在这两个类中都只有两个重载的方法: lock()方法和tryAcquire(int acquires)方法。如下图: ![这里写图片描述][SouthEast 2] ## 5. 重点方法: ## 这里只分析加锁和释放锁两个方法: lock() unlock() ## 1)ReentrantLock.lock()方法 ## 在ReentrantLock类中的lock()方法很简单,直接调用了同步器Sync的lock()方法,源码如下: public void lock() { sync.lock(); } 我们直接看Sync中的lock()方法,通过构造器我们可知,我们使用的同步器默认是NonfairSync,也可以通过指定也可以使用FairSync。我们就把这两个类中的 lock()函数源码给出做个对比: //NonfairSync类中 final void lock() { //首先让当前线程获取锁 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else//当前线程获取锁失败之后再从同步队列中取出线程来获取锁 acquire(1); } //FairSync类中 final void lock() { acquire(1); } 逻辑分析: (1)NonfairSync非公平锁中,由源码可知他不是按照请求顺序来给线程分配锁的,而是允许”插队”,当一个线程请求非公平的锁时,如果在发出请求的同时,该锁的状态变成了可用,那么该线程将跳过队列中所有等待线程并获得锁,这样就避免了线程的切换。 对于当前线程获取锁:如果锁可用,通过维护AQS中的state属性,通过CAS设置state值由0到1。否则调用acquire(1)函数,这个函数的实现具体可看AQS源码解析那一节:[http://blog.csdn.net/u010853261/article/details/54747421][http_blog.csdn.net_u010853261_article_details_54747421] (2)FairSync公平锁中,直接从同步队列中按照请求顺序来获取锁。 ## 2)ReentrantLock.unlock()方法 ## 对于unlock()方法,在NonfairSync和FairSync中没有什么区别。都是调用AQS中的release()方法。 public void unlock() { sync.release(1); } release()在AQS这篇博文中也有讲。传送门: [http://blog.csdn.net/u010853261/article/details/54747421][http_blog.csdn.net_u010853261_article_details_54747421] [SouthEast]: /images/20220711/511e69a5afd945eb9c751ed88ff08ba3.png [SouthEast 1]: /images/20220711/cdee52950c4d49eb95ed0c835973de23.png [SouthEast 2]: /images/20220711/366c92476ac5498a88fc6c72db607483.png [http_blog.csdn.net_u010853261_article_details_54747421]: http://blog.csdn.net/u010853261/article/details/54747421
还没有评论,来说两句吧...