在 Java 多线程中, 可以使用 synchronized 关键字来实现多线程之间同步互斥, 但在 JDK 1.5 中新增加了 ReentrantLock 类也能达到同样的效果, 并且在扩展功能上也更加强大, 比如具有嗅探锁定, 多路分支通知, 公平锁和非公平锁等(默认)功能, 而且在使用上也比 synchronized 更加的灵活.。

文章目录

  • ReentrantLock简介
  • ReenTrantLock和synchronized的区别
  • ReentrantLock常用方法
  • ReentrantLock示例
  • 重入性的实现原理
  • 公平锁与非公平锁
  • 本文小结

ReentrantLock简介

ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。在java关键字synchronized隐式支持重入性,synchronized通过获取自增,释放自减的方式实现重入。与此同时,ReentrantLock还支持公平锁和非公平锁两种方式。公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平性锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量。


ReenTrantLock和synchronized的区别

在Java里一共有两类锁, 一类是synchornized同步锁,还有一种是JUC里提供的锁Lock,Lock是个接口,其核心实现类就是ReentrantLock。ReentrantLock的实现 ,主要是采用自旋锁,循环调用CAS操作来实现加锁,避免了使线程进入内核态的阻塞状态。

ReenTrantLock和synchronized的比较

ReentrantLock独有的功能

  1. 可指定是公平锁还是非公平锁,所谓公平锁就是先等待的线程先获得锁
  2. 提供了一个Condition类,可以分组唤醒需要唤醒的线程
  3. 提供能够中断等待锁的线程的机制,lock.lockInterruptibly()

ReentrantLock常用方法


常用方法其解释

void  lock()   //加锁
void  unlock()  //释放锁
boolean isHeldByCurrentThread();   // 当前线程是否保持锁定
boolean isLocked()  // 是否存在任意线程持有锁资源
void lockInterruptbly()  // 如果当前线程未被中断,则获取锁定;如果已中断,则抛出异常(InterruptedException)
int getHoldCount()   // 查询当前线程保持此锁定的个数,即调用lock()方法的次数
int getQueueLength()   // 返回正等待获取此锁定的预估线程数
int getWaitQueueLength(Condition condition)  // 返回与此锁定相关的约定condition的线程预估数
boolean hasQueuedThread(Thread thread)  // 当前线程是否在等待获取锁资源
boolean hasQueuedThreads()  // 是否有线程在等待获取锁资源
boolean hasWaiters(Condition condition)  // 是否存在指定Condition的线程正在等待锁资源
boolean isFair()   // 是否使用的是公平锁

ReentrantLock示例

ReentrantLock代码示例

package cn.wideth.util;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockCount implements Runnable{//共享资源(临界资源)private static int count=0;//引入一个重入锁private static Lock lock = new ReentrantLock();public void increase(){lock.lock();count++;lock.unlock();}@Overridepublic void run() {for(int i=0;i<500000;i++){increase();}}/*** join方法的作用是将子线程加入主线* 程,等子线程结束以后,主线程才结束* @param args* @throws InterruptedException*/public static void main(String[] args) throws InterruptedException{//一个实例对象ReentrantLockCount instance=new ReentrantLockCount();//两个线程Thread t1=new Thread(instance);Thread t2=new Thread(instance);t1.start();t2.start();t1.join();t2.join();System.out.println(count);}
}

运行结果:


重入性的实现原理

要想支持重入性,就要解决两个问题:1. 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功;2. 由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功。我们知道,同步组件主要是通过重写AQS的几个protected方法来表达自己的同步语义。针对第一个问题,我们来看看ReentrantLock是怎样实现的,以非公平锁为例,判断当前线程能否获得锁为例,核心方法为nonfairTryAcquire:

final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();//1. 如果该锁未被任何线程占有,该锁能被当前线程获取if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}//2.若被占有,检查占有线程是否是当前线程else if (current == getExclusiveOwnerThread()) {// 3. 再次获取,计数加一int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;
}

这段代码的逻辑也很简单,具体请看注释。为了支持重入性,在第二步增加了处理逻辑,如果该锁已经被线程所占有了,会继续检查占有线程是否为当前线程,如果是的话,同步状态加1返回true,表示可以再次获取成功。每次重新获取都会对同步状态进行加一的操作,那么释放的时候处理思路是怎样的了?(依然还是以非公平锁为例)核心方法为tryRelease:

protected final boolean tryRelease(int releases) {//1. 同步状态减1int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {//2. 只有当同步状态为0时,锁成功被释放,返回truefree = true;setExclusiveOwnerThread(null);}// 3. 锁未被完全释放,返回falsesetState(c);return free;
}

代码的逻辑请看注释,需要注意的是,重入锁的释放必须得等到同步状态为0时锁才算成功释放,否则锁仍未释放。如果锁被获取n次,释放了n-1次,该锁未完全释放返回false,只有被释放n次才算成功释放,返回true。到现在我们可以理清ReentrantLock重入性的实现了,也就是理解了同步语义的第一条。


公平锁与非公平锁

ReentrantLock支持两种锁:公平锁和非公平锁。何谓公平性,是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求上的绝对时间顺序,满足FIFO。ReentrantLock的构造方法无参时是构造非公平锁,源码为:

public ReentrantLock() {sync = new NonfairSync();
}

另外还提供了另外一种方式,可传入一个boolean值,true时为公平锁,false时为非公平锁,源码为:

public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}

在上面非公平锁获取时(nonfairTryAcquire方法)只是简单的获取了一下当前状态做了一些逻辑处理,并没有考虑到当前同步队列中线程等待的情况。我们来看看非公平锁的处理逻辑是怎样的,核心方法为:

 /*** Performs non-fair tryLock.  tryAcquire is implemented in* subclasses, but both need nonfair try for trylock method.*/final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}

公平锁每次都是从同步队列中的第一个节点获取到锁,而非公平性锁则不一定,有可能刚释放锁的线程能再次获取到锁。

公平锁 VS 非公平锁

  1. 公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象
  2. 公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。因此,ReentrantLock默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量

.

本文小结

本文详细描述了重入锁ReentrantLock的相关知识,介绍了重入锁的知识,公平锁以及非公平锁的的知识,最后给了一个使用案例,最后分析了ReentrantLock底层的实现原理。

浅析ReentrantLock重入锁相关推荐

  1. ReentrantLock重入锁

    重入锁,表示支持重新进入的锁,也就是说,如果当前线程t1通过调用lock方法获取了锁之后,再次调用lock,是不会再阻塞去获取锁的,直接增加重试次数就行了.synchronized和Reentrant ...

  2. Java并发编程之ReentrantLock重入锁

    ReentrantLock: 源码层面分析: public class ReentrantLock implements Lock, java.io.Serializable {private sta ...

  3. 从源码角度彻底理解ReentrantLock(重入锁)

    源码分析ReentrantLock https://blog.csdn.net/lxltmac/article/details/84871929白话讲解lock

  4. 死磕Java并发:J.U.C之重入锁:ReentrantLock

    ReentrantLock,可重入锁,是一种递归无阻塞的同步机制.它可以等同于synchronized的使用,但是ReentrantLock提供了比synchronized更强大.灵活的锁机制,可以减 ...

  5. 【并发编程】线程锁--Synchronized、ReentrantLock(可重入锁)

    在说锁之前,我们要明白为什么要加锁,不加锁会怎样? 在并发编程中,很容易出现线程安全问题,接下来我们看个很经典的例子--银行取钱,来看一下有关线程安全的问题. 取钱的流程可以分为一下几个步骤: 1.用 ...

  6. 并发编程-19AQS同步组件之重入锁ReentrantLock、 读写锁ReentrantReadWriteLock、Condition

    文章目录 J.U.C脑图 ReentrantLock概述 ReentrantLock 常用方法 synchronized 和 ReentrantLock的比较 ReentrantLock示例 读写锁R ...

  7. 重入锁:ReentrantLock 详解

    在JDK5.0版本之前,重入锁的性能远远好于synchronized关键字,JDK6.0版本之后synchronized 得到了大量的优化,二者性能也不分伯仲,但是重入锁是可以完全替代synchron ...

  8. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  9. java condition_(原创)Java的ReentrantLock(可重入锁)下的Condition

    先来看一下这个Condition的使用场景,在LinkedBlockingQueue(链表的阻塞队列)类中包含如下的定义,通过使用lock.newCondition()方法,可以获得一个Conditi ...

最新文章

  1. 机器学习验证集为什么不再有新意?
  2. runtime的常用方法objc_setAssociatedObject的使用
  3. 前端性能优化:当页面渲染遇上边缘计算
  4. Linux DMA 内存拷贝与memcpy 速率比较
  5. [攻防世界 pwn]——pwn-200
  6. 公司技术管理角度看C++游戏程序员发展
  7. string contains不区分大小写_String基础复习
  8. linux 机器之间 zssh, rz, sz互相传输 ( How to install zssh in Ubuntu 13.10 (Saucy))
  9. Git笔记(27) 储藏与清理
  10. 为什么中国有很大一部分人不愿使用windows10?
  11. 知道你为什么富不起来吗 十个耽误你一生的缺点 穷人杀手[网摘]
  12. heartbeat 高可用工具
  13. python破解b站验证码实现登陆
  14. 6款反垃圾邮件产品横向比较测试
  15. Byond公司发布BIS平台,未来开发VR、AR不再繁杂
  16. Java实现 LeetCode 686 重复叠加字符串匹配
  17. 7-8 打印九九口诀表(15 分)
  18. fs和php的区别,对比爆料评测美的wfs4037跟wfs5017哪个好?区别是?良心评测点评...
  19. 基于爬虫+人脸识别库实现指定人物自动采集
  20. JPEG算法解密 JPEG原理详解 (转载 by jinchao)

热门文章

  1. c#编程指南(四) 组元(Tuple)
  2. Cocos2d-x 3.2:定时器的使用和原理探究(2)
  3. Hive UDAF开发
  4. 重新安装python2.6 和 yum (不可以直接安装yum yum 依赖于python2.6)
  5. 一些服务器客户端的c例子
  6. U盘安装Ubuntu14.04 server版 提示无法挂载cd-rom数据的解决办法
  7. Ubuntu下安装中文输入法
  8. android.graphics包中的一些类的使用
  9. linux 无线命令
  10. [转]直接拿来用!最火的Android开源项目(一)