并发编程系列之Lock锁可重入性与公平性
并发编程系列之Lock锁可重入性与公平性
一、相似之处:Lock锁 vs Synchronized 代码块Lock锁是一种类似于synchronized 同步代码块的线程同步机制。从Java 5开始java.util.concurrent.locks
引入了若干个Lock锁的实现类,所以通常情况下我们不需要实现自己的锁,重要的是需要知道如何使用它们,了解它们实现背后的原理。
Lock锁API的基本使用方法和Synchronized 关键字大同小异,代码如下
Lock lock = new ReentrantLock(); //实例化锁//lock.lock(); //上锁boolean locked = lock.tryLock(); //尝试上锁if(locked){ try { //被锁定的同步代码块,同时只能被一个线程执行 }finally { lock.unlock(); //放在finally代码块中,保证锁一定会被释放 }}
synchronized(obj){ //被锁定的同步代码块,同时只能被一个线程执行}
Lock锁使用看上去麻烦一点,但是java默认提供了很多Lock锁,能满足更多的应用场景。比如:基于信号量加锁、读写锁等等,关注我的专栏《java并发编程》,后续都会介绍。
二、Lock接口中的方法Lock接口实现方法通常会维护一个计数器,当计数器=0的时候资源被释放,当计数器大于1的时候资源被锁定。
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition();}
lock() - 调用该方法会使锁定计数器增加1,如果此时共享资源是空闲的,则将锁交给调用该方法的线程。unlock() - 调用该方法使锁定计数器减少1,当锁定计数器为0时,资源被释放。tryLock() - 如果该资源是空闲的,那么调用该方法将返回true,锁定计数器将增加1。如果资源处于被占用状态,那么该方法返回false,但是线程将不被阻塞。tryLock(long timeout, TimeUnit unit) - 按照该方法尝试获得锁,如果资源此时被占用,线程在退出前等待一定的时间段,该时间段由该方法的参数定义,以期望在此时间内获得资源锁。lockInterruptibly() - 如果资源是空闲的,该方法会获取锁,同时允许线程在获取资源时被其他线程打断。这意味着,如果当前线程正在等待一个锁,但其他线程要求获得该锁,那么当前线程将被中断,并立即返回不会获得锁。三、不同点:Lock锁 vs Synchronized 代码块使用synchronized同步块和使用Lock API 之间还是有一些区别的
一个synchronized同步块必须完全包含在一个方法中 - 但Lock API的lock()和unlock()操作,可以在不同的方法中进行synchronized同步块不支持公平性原则,任何线程都可以在释放后重新获得锁,不能指定优先级。但我们可以通过指定fairness 属性在Lock API中实现公平的优先级,可以实现等待时间最长的线程被赋予对锁的占有权。如果一个线程无法访问synchronized同步块,它就会被阻塞等待。Lock API提供了tryLock()方法,尝试获取锁对象,获取到锁返回true,否则返回false。返回false并不阻塞线程,所以使用该方法可以减少等待锁的线程的阻塞时间。四、锁的可重入性”可重入“意味着某个线程可以安全地多次获得同一个锁对象,而不会造成死锁。
4.1. synchronized锁的可重入性下面的代码synchronized代码块嵌套synchronized代码块,锁定同一个this对象,不会产生死锁。证明synchronized代码块针对同一个对象加锁,是可重入的。
public void testLock(){ synchronized (this) { System.out.println("第1次获取锁,锁对象是:" + this); int index = 1; do { synchronized (this) { System.out.println("第" + (++index) + "次获取锁,锁对象是:" + this); } } while (index != 10); }}
上面的这段代码输出结果是
第1次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116第2次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116第3次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116第4次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116第5次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116第6次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116第7次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116第8次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116第9次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116第10次获取锁,锁对象是:com.example.demo.thread.TestLockReentrant@769c9116
4.2.ReentrantLock可重入锁Lock接口的实现类ReentrantLock,也是可重入锁。一般来说类名包含Reentrant的Lock接口实现类实现的锁都是可重入的。
public void testLock1(){ Lock lock = new ReentrantLock(); //实例化锁 lock.lock(); //上锁 System.out.println("第1次获取锁,锁对象是:" + lock); try { int index = 1; do { lock.lock(); //上锁 try { System.out.println("第" + (++index) + "次获取锁,锁对象是:" + lock); }finally { lock.unlock(); } } while (index != 10); }finally { lock.unlock(); //放在finally代码块中,保证锁一定会被释放 }}
当线程第一次获得锁的时候,计数器被设置为1。在解锁之前,该线程可以再次获得锁,每次计数器都会增加1。对于每一个解锁操作,计数器被递减1,当计数器为0时锁定资源被释放。所以最重要的是:lock(tryLock)要与unlock方法成对出现,即:在代码中加锁一次就必须解锁一次,否则就死锁
五、Lock锁的公平性Java的synchronized 同步块对试图进入它们的线程,被授予访问权(占有权)的优先级顺序没有任何保证。因此如果许多线程不断争夺对同一个synchronized 同步块的访问权,就有可能有一个或多个线程从未被授予访问权。这就造成了所谓的 "线程饥饿"。为了避免这种情况,锁应该是公平的。
Lock lock = new ReentrantLock(true);
可重入锁提供了一个公平性参数fairness ,通过该参数Lock锁将遵守锁请求的顺序,即在一个线程解锁资源后,锁将被交给等待时间最长的线程。这种公平模式是通过在锁的构造函数中传递 "true "来设置的。
欢迎关注我的博客,更多精品知识合集本文转载注明出处(必须带连接,不能只转文字):字母哥博客 - zimug.com
觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力!。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。
《kafka修炼之道》《手摸手教你学Spring Boot2.0》《Spring Security-JWT-OAuth2一本通》《实战前后端分离RBAC权限管理系统》《实战SpringCloud微服务从青铜到王者》并发编程系列之Lock锁可重入性与公平性 相关文章
- 信号之函数的可重入性
信号之函数的可重入性 信号之函数的可重入性 在调用某个函数过程中出现中断信号,且改信号处理函数中再次调用该函数,访问全局、静态变量的函数是不可重入函数。 前后数据不一致,函数是不可重入的,特点:函数中使用...
- 2020秋招最新面试题并发编程高频面试题可重入锁+线程池+内存模型
2020秋招最新面试题:并发编程高频面试题:可重入锁+线程池+内存模型等(含答案) 对于一个Java程序员而言,能否熟练掌握并发编程是判断他优秀与否的重要标准之一。因为并发编程是Java语言中最为晦涩的知识点,它涉及操作系...
- 高并发编程系列全面剖析Java并发编程之AQS的核心实现
高并发编程系列:全面剖析Java并发编程之AQS的核心实现 在并发编程领域,AQS号称是并发同步组件的基石,很多并发同步组件都是基于AQS实现,所以想掌握好高并发编程,你需要掌握好AQS。 本篇主要通过对AQS的实现原理、数据模...
- 可重入与不可重入
可重入与不可重入的理解 可重入的意思是一个函数同时被多次调用,在恢复上次不会影响原来的操纵结果 1、全局变量是不可重入, printf 引用全局变量stdout,浮点数是把临时操作结果放到硬件寄存器,会导致被破坏。 printf和浮...
- Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步
Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步 阻塞 非阻塞 进程 线程 协程... 1 引言 并发、并行、串行、同步、异步、阻塞、非阻塞、进程、线程、协程是并发编程中的常见概念,相似却也有却不尽相同,令人...
- 可重入与不可重入函数的区别
可重入与不可重入函数的区别 关注、 星标公众 号 ,不错过精彩内容 素材来源:网络 编辑整理:strongerHuang 可重入和不可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在...
- 并发编程JMM系列之基础!
并发编程JMM系列之基础! 并发编程JMM系列之基础! 来源 | 公众号 | Justin的后端书架 Java程序员在进行多线程开发时,并不需要关心线程间是如何通信的,这些对程序员本来来说完全是透明的,但是内存可见性问题很容易让我们...
- Java并发编程系列之Semaphore详解
Java并发编程系列之Semaphore详解 简单介绍 我们以饭店为例,假设饭店只有三个座位,一开始三个座位都是空的。这时如果同时来了三个客人,服务员人允许其中他们进去用餐,然后对外说暂无座位。后来的客人必须在门口等待,...
- 可重入锁ReentrantLock-复习
可重入锁ReentrantLock-复习 可重入锁ReentrantLock,这个锁的同步功能比synchronized更灵活,颗粒度更细: 可重入锁可以提供读写锁; 可以提供Condition条件锁对象,针对不同的条件进行加锁。但是加锁的动作还是由ReenTrantLock对象来做,C...
- 可重入函数
可重入函数 如上图所示,main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步时,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换到sighandler函数,sighandler也...
- 「BATJ面试系列」并发编程之happens-before详解
「BATJ面试系列」并发编程之happens-before详解 点关注,不迷路;持续更新Java相关技术及资讯!!! 从JDK 5 开始,JMM使用happens-before的概念来阐述多线程之间的内存可见性。 在JMM中,如果一个操作执行的结果需要对另一个操作可见...
- java可重入锁ReentrantLock原理
java可重入锁ReentrantLock原理 概念 :再次获得自己的内部锁。比如当一个线程获得某个对象的锁后,还没有释放,还想再次获得这个对象的锁时还可以获得。 网上看到又去的故事阐述这个过程很有趣: 轻松学习java可重入锁(Reentra...
- Golang+Redis可重入锁
概念 计算机科学中, 可重入互斥锁 (英:reentrant mutex)是互斥锁的一种,同一线程对其多次加锁不会产生死锁。可重入互斥锁也称 递归互斥锁 (英:recursive mutex)或 递归锁 (英:recursive lock)。 如果对已经上锁的普...
- (十六)ReentrantLock可重入锁使用和介绍
1、ReentrantLock介绍 jdk中独占锁的实现除了使用关键字 synchronized 外,还可以使用 ReentrantLock 。 虽然在性能上 ReentrantLock 和 synchronized 没有什么区别,但 ReentrantLock 相比 synchronized 而言功能更加丰富,使用起来更为灵活,也更适合复...
- 线程安全和可重入函数
线程安全和可重入函数 一,什么是线程安全? 1,线程安全就是说多线程访问同一代码,不会产生不确定的结果。换句话说,线程安全就是多线程访问时,采用加锁机制,当一个线程访问该类的某个数据时,用锁对数据进行保护...
- 线程安全与可重入函数
线程安全与可重入函数 在之前的博文中有说过线程,线程是一个执行流,一个指令序列,一个指令分支,为了共享资源而产生的线程,但只要是资源,都会出现资源的数目与操作安全问题,所以在线程这一块,设计了信号量,...
- 可重入 threadsafe reentrant nonreentrant
\threadsafe The \threadsafe command includes a line in the documentation to indicate that the associated class or function is threadsafe and can be called simultaneously by multiple threads, even when separate invocations reference shared
- 线程安全和可重入函数的对比
线程安全和可重入函数的对比 可重入函数 可重入函数简单也就相当于可以被中断的函数,即一个函数正在执行的过程中被其他函数或操作中断出去,在这个中断操作执行完毕后,再返回来执行原代码。 那么如果这个中断出去的...
- 【Linux】线程安全和可重入函数
【Linux】线程安全和可重入函数 线程安全 所谓线程安全,就是当多个线程访问同一个数据时,不会造成数据出错。其实它是采用了加锁的机制来保证在一个线程访问该数据时,其他的线程不可以访问,直到等到那个线程访问结...
- java并发编程的艺术-Lock
java并发编程的艺术-Lock Java并发编程的艺术-Lock 1、锁的内存语义 锁可以让临界区互斥执行。 对一个锁的解锁,happens-before与随后这个锁的加锁操作。 当线程释放锁时,JMM会把线程对应的本地内存中的共享变量刷回主内存,当另...