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

🚀 优质资源分享 🚀

学习路线指引(点击解锁)知识定位人群定位
🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

一、相似之处: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微服务从青铜到王者》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401031.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyEclipse10破解详细说明

MyEclipse10破解详细教程 1.先安装好jdk,再安装好myeclipse10,并关闭该软件(jdk下载安装教程 http://blog.csdn.net/qq_36330228/article/details/75268398) 2.下载破解工具,打开me_active.jar 3.输入任意用户名,点击Systemid按…

DirectXInput

DirectXInput 作为DirectX的提供的很多外接技术输入设备的功能功能函数。 DirectXInput转载于:https://www.cnblogs.com/yshic/archive/2012/12/05/2802854.html

中国电子云数据库 Mesh 项目 DBPack 的实践

🚀 优质资源分享 🚀 学习路线指引(点击解锁)知识定位人群定位🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一…

第三章DOM

1. DOM的概念 D:Document. O:Object.对象可以分为三类, 1. 用户自定义的对象。 2. 内建对象,如Array,Math,Date。 3. 宿主对象,浏览器提供的对象。如window对象,方法例如window.open,window.blur. M:Model,…

grpc ssl使用

相关链接 http://www.jianshu.com/p/2873a8349ca0 转载于:https://www.cnblogs.com/freedommovie/p/6810301.html

(百度、谷歌)地图经纬度gps偏移解决办法:gps纠偏数据库纠偏

使用方法: 1、假设原始gps数据为(73.528888,39.368888) 2、查询数据库:SELECT * FROM offset where lng73.52 and lat39.36,得出offsetlat、offsetlng 3、用原始的lat加上offsetlat,lng加上offs…

国产开源优秀新一代MPP数据库StarRocks入门之旅-数仓新利器(中)

🚀 优质资源分享 🚀 学习路线指引(点击解锁)知识定位人群定位🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一…

使用 Pandas 分析 Apache 日志

本文的作者是 Nikolay Koldunov,本文原文是Apache log analysis with Pandas 注本文的图有问题,没法引用,还是去原文看下,这里作为一个引子。 %pylab inline欢迎来到 pylab,一个基于 matplotlib 的 Python 环境【backe…

IOS UIPageController

- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //定义UIScrollView //self.scrollView [[UIScrollView alloc] init]; //self.scrollView.frame CGRectMake(10, 0, 200, 500); self.scrollView.con…

jquery 统计统计子标签的个数

本文实例讲述了jQuery获得子元素个数的方法。分享给大家供大家参考。具体分析如下://获取iddiv1下的子元素的个数$(#div1).children().length;//获取iddiv1下的span元素个数$(#div1).children(span).length;$(".site_show").children().length18$(".…

(数据科学学习手札136)Python中基于joblib实现极简并行计算加速

🚀 优质资源分享 🚀 学习路线指引(点击解锁)知识定位人群定位🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一…

Redis的编译安装

介绍redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交…

C# 使用微软的Visual Studio International Pack 类库提取汉字拼音首字母

昨天经过网友提醒,提取汉字拼音的方法可以使用微软的一个类库 Visual Studio International Pack ,今天试了一试,确实好用!下面分享下使用方法: 首先下载Visual Studio International Pack 1.0,官方下载地址…

渗透测试集成环境Faraday

渗透测试集成环境FaradayKali Linux集成了海量的渗透测试工具。但是这些工具在使用的时候,还是分离的。虽然用户可以通过Shell、日志/报告导入导出功能等方式,进行整合,但是仍然不便于分析。Faraday提出了IPE(Integrated Penetrat…

docker 1.2 之docker基本用法

🚀 优质资源分享 🚀 学习路线指引(点击解锁)知识定位人群定位🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一…

date数据要在前台显示

date数据要在前台显示,如果要截取一部分使用,最好用SimpleDateFormat不要用toString不牢靠。 转载于:https://www.cnblogs.com/king12345678/archive/2012/12/07/2807604.html

被迫开始学习Typescript —— class

🚀 优质资源分享 🚀 学习路线指引(点击解锁)知识定位人群定位🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一…

ASP.NET实现二维码(QRCode)的创建和读取

一、项目引用QRCode的DLL文件&#xff08;ThoughtWorks.QRCode.dll&#xff09; 二、ASPX页面&#xff08;两个jquery的js文件请自行去官网下载&#xff09;&#xff1a;[html] <html xmlns"http://www.w3.org/1999/xhtml"> <head runat"server"…

如何查找程序bug

方法一&#xff1a;记录程序运行日志。 在程序加入一些日志信息&#xff0c;记录下程序的运行状态&#xff0c;据此定位bug。 方法二&#xff1a;不断缩小调试范围。 注释掉部分代码。开始时可以多注释掉一些代码&#xff0c;调试没有问题了再逐渐缩小范围。直到最后准确定位bu…

二维数组转变成一维

var arr [1, 2, 3,[false], [hello]];var arr2 [].concat.apply([], arr);console.log(arr2); // [ 1, 2, 3, false, hello ] 转载于:https://www.cnblogs.com/ax-null/p/6821165.html