并发编程系列之Lock锁可重入性与公平性

作者:神秘网友 发布时间:2022-05-13 07:13:04

并发编程系列之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锁可重入性与公平性 相关文章

  1. 信号之函数的可重入性

    信号之函数的可重入性 信号之函数的可重入性 在调用某个函数过程中出现中断信号,且改信号处理函数中再次调用该函数,访问全局、静态变量的函数是不可重入函数。 前后数据不一致,函数是不可重入的,特点:函数中使用...

  2. 2020秋招最新面试题并发编程高频面试题可重入锁+线程池+内存模型

    2020秋招最新面试题:并发编程高频面试题:可重入锁+线程池+内存模型等(含答案) 对于一个Java程序员而言,能否熟练掌握并发编程是判断他优秀与否的重要标准之一。因为并发编程是Java语言中最为晦涩的知识点,它涉及操作系...

  3. 高并发编程系列全面剖析Java并发编程之AQS的核心实现

    高并发编程系列:全面剖析Java并发编程之AQS的核心实现 在并发编程领域,AQS号称是并发同步组件的基石,很多并发同步组件都是基于AQS实现,所以想掌握好高并发编程,你需要掌握好AQS。 本篇主要通过对AQS的实现原理、数据模...

  4. 可重入与不可重入

    可重入与不可重入的理解 可重入的意思是一个函数同时被多次调用,在恢复上次不会影响原来的操纵结果 1、全局变量是不可重入, printf 引用全局变量stdout,浮点数是把临时操作结果放到硬件寄存器,会导致被破坏。 printf和浮...

  5. Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步

    Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步 阻塞 非阻塞 进程 线程 协程... 1 引言 并发、并行、串行、同步、异步、阻塞、非阻塞、进程、线程、协程是并发编程中的常见概念,相似却也有却不尽相同,令人...

  6. 可重入与不可重入函数的区别

    可重入与不可重入函数的区别 关注、 星标公众 号 ,不错过精彩内容 素材来源:网络 编辑整理:strongerHuang 可重入和不可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在...

  7. 并发编程JMM系列之基础!

    并发编程JMM系列之基础! 并发编程JMM系列之基础! 来源 | 公众号 | Justin的后端书架 Java程序员在进行多线程开发时,并不需要关心线程间是如何通信的,这些对程序员本来来说完全是透明的,但是内存可见性问题很容易让我们...

  8. Java并发编程系列之Semaphore详解

    Java并发编程系列之Semaphore详解 简单介绍 我们以饭店为例,假设饭店只有三个座位,一开始三个座位都是空的。这时如果同时来了三个客人,服务员人允许其中他们进去用餐,然后对外说暂无座位。后来的客人必须在门口等待,...

  9. 可重入锁ReentrantLock-复习

    可重入锁ReentrantLock-复习 可重入锁ReentrantLock,这个锁的同步功能比synchronized更灵活,颗粒度更细: 可重入锁可以提供读写锁; 可以提供Condition条件锁对象,针对不同的条件进行加锁。但是加锁的动作还是由ReenTrantLock对象来做,C...

  10. 可重入函数

    可重入函数 如上图所示,main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步时,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换到sighandler函数,sighandler也...

  11. 「BATJ面试系列」并发编程之happens-before详解

    「BATJ面试系列」并发编程之happens-before详解 点关注,不迷路;持续更新Java相关技术及资讯!!! 从JDK 5 开始,JMM使用happens-before的概念来阐述多线程之间的内存可见性。 在JMM中,如果一个操作执行的结果需要对另一个操作可见...

  12. java可重入锁ReentrantLock原理

    java可重入锁ReentrantLock原理 概念 :再次获得自己的内部锁。比如当一个线程获得某个对象的锁后,还没有释放,还想再次获得这个对象的锁时还可以获得。 网上看到又去的故事阐述这个过程很有趣: 轻松学习java可重入锁(Reentra...

  13. Golang+Redis可重入锁

    概念 计算机科学中, 可重入互斥锁 (英:reentrant mutex)是互斥锁的一种,同一线程对其多次加锁不会产生死锁。可重入互斥锁也称 递归互斥锁 (英:recursive mutex)或 递归锁 (英:recursive lock)。 如果对已经上锁的普...

  14. (十六)ReentrantLock可重入锁使用和介绍

    1、ReentrantLock介绍 jdk中独占锁的实现除了使用关键字 synchronized 外,还可以使用 ReentrantLock 。 虽然在性能上 ReentrantLock 和 synchronized 没有什么区别,但 ReentrantLock 相比 synchronized 而言功能更加丰富,使用起来更为灵活,也更适合复...

  15. 线程安全和可重入函数

    线程安全和可重入函数 一,什么是线程安全? 1,线程安全就是说多线程访问同一代码,不会产生不确定的结果。换句话说,线程安全就是多线程访问时,采用加锁机制,当一个线程访问该类的某个数据时,用锁对数据进行保护...

  16. 线程安全与可重入函数

    线程安全与可重入函数 在之前的博文中有说过线程,线程是一个执行流,一个指令序列,一个指令分支,为了共享资源而产生的线程,但只要是资源,都会出现资源的数目与操作安全问题,所以在线程这一块,设计了信号量,...

  17. 可重入 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

  18. 线程安全和可重入函数的对比

    线程安全和可重入函数的对比 可重入函数 可重入函数简单也就相当于可以被中断的函数,即一个函数正在执行的过程中被其他函数或操作中断出去,在这个中断操作执行完毕后,再返回来执行原代码。 那么如果这个中断出去的...

  19. 【Linux】线程安全和可重入函数

    【Linux】线程安全和可重入函数 线程安全 所谓线程安全,就是当多个线程访问同一个数据时,不会造成数据出错。其实它是采用了加锁的机制来保证在一个线程访问该数据时,其他的线程不可以访问,直到等到那个线程访问结...

  20. java并发编程的艺术-Lock

    java并发编程的艺术-Lock Java并发编程的艺术-Lock 1、锁的内存语义 锁可以让临界区互斥执行。 对一个锁的解锁,happens-before与随后这个锁的加锁操作。 当线程释放锁时,JMM会把线程对应的本地内存中的共享变量刷回主内存,当另...

每天更新java,php,javaScript,go,python,nodejs,vue,android,mysql等相关技术教程,教程由网友分享而来,欢迎大家分享IT技术教程到本站,帮助自己同时也帮助他人!

Copyright 2021, All Rights Reserved. Powered by 跳墙网(www.tqwba.com)|网站地图|关键词