Java并发编程实战——显示锁
显示锁
1.1 Lock与ReentrantLocksynchronized 的局限性 与 Lock 的优点
- 内置锁的局限性:1、无法中断一个正在等待获取锁的线程; 2. 无法在请求获取一个锁时无限地等待下去?3. 内置锁无法响应中断
- 内置锁的好处:与异常处理操作实现了很好的交互,当占有锁线程执行发生异常,此时JVM会让线程自动释放锁;而Lock的加锁和释放锁都需要手动执行,并且需要考虑抛出异常的情况,通常在finally代码块中释放锁。
- ReentrantLock锁实现了Lock接口,更灵活,用途更广泛;
1.1.1 轮询锁与定时锁
- 内置锁 开始请求锁后,这个操作将无法取消
- 可定时的与可轮询的锁获取方式是由tryLock方法实现的,与无条件的锁获取方式相比,它具有更完善的错误回复机制
- 避免死锁:轮询锁破坏请求保持条件:只要有一个资源得不到分配,也不给这个进程分配其他的资源;例如使用tryLock来获取两个锁,如果不能同时获得,那么就回退并重新尝试。
- boolean tryLock():仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。
- boolean tryLock(long time, TimeUnit unit) :tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
- 在内置锁中,释放锁的操作总是与获取锁的操作处于同一个代码块
1.1.2 可中断的锁获取操作
- 基于lockInterruptibly方法;
首先你得了解为什么Lock要提供tryLock和tryInterruptibly因为它要破坏死锁中的“不可抢占条件”。
synchronized没办法破坏“不可抢占条件”,synchronized申请资源的时候,如果申请不到,线程直接进入阻塞状态,无法释放已经占有的锁。
但我们希望的是:如果占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占条件被破坏掉了。所以,Lock提供了三种方法,均可以破坏不可抢占条件。这也是为什么有了synchronized还要搞一个Lock的原因之一。
lock():拿不到lock就不罢休,不然线程就一直block。
tryLock:立即返回,获得锁返回true, 没获得锁返回false;
tryLock(long time, TimeUint unit):拿不到lock,就等一段时间,超时返回false
tryInterruptibly:在锁上等待,直到获取锁,但是会响应中断,这个方法优先考虑响应中断,而不是响应锁的普通获取或重入获取。
- 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),对该线程调用interrupt()方法(中断-java中停止线程的机制)后,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。
// ...@Overridepublic void run(){try{// 你想做的事}catch(InterruptedException e){// 捕获异常后的代码处理}// 以及接下来的部分}
//
1.1.3 非块结构的加锁
- 对链表的每个节点进行加锁,称为连锁式加锁(持有当前节点的锁,释放上一节点的锁,获取下一个节点的锁。使得不同的线程能独立地对链表的不同部分进行操作)
1.2 性能因素考虑
- Java 5.0(JDK 1.5) ReetrantLock加入,比内置锁性能更好
- Java 6.0 (JDK 1.8) 对内置锁做了算法改进,性能与显示锁差不多
1.3 公平性
ReentrantLock的构造函数中提供了两种公平性选择:创建一个非公平的锁(默认)或者一个公平的锁;
内置锁是非公平锁;
所谓公平锁就是,线程将按照它们发出的请求顺序来获取锁;而非公平锁,则允许“插队”;
另外,还有放入队列的时机不一样;在公平的锁中,如果有另一个线程持有这个锁或者有其他线程在队列等待这个锁,那么新发出请求的线程将被放入队列中;而在非公平的锁中,只有当锁被某个线程持有时,新发出请求的线程才会被放入队列中。
在大多数情况下,非公平的锁性能会高于公平锁;原因是在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟,假设线程A持有一个锁,并且线程B在请求这个锁,由于锁被A占有,B被挂起,当线程A释放锁时,线程B将被唤醒。与此同时,有一个线程C也请求这个锁,那么可能会在被挂起线程B完全恢复之前获得、使用以及释放这个锁。这种情况是一种双赢的局面,B获得的锁时刻并没有延迟,线程C也更早地获得了锁,吞吐量得到了提升
设计规则与思维模式:什么时候使用公平锁?
当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,应该使用公平锁。因为这种情况下,线程C难以在短的时间内获得、使用以及释放这个锁,无法带来吞吐量的提升。
Java X=Java SE X=JDK1.X
1.4 在sychronized和ReentrantLock之间选择
- 就性能而言,ReentrantLock 在java 5.0版本是远远胜出内置锁,而在java6.0版本只是略微胜出
- 本书作者的观点是:
- 仅当内置锁不能满足一些高级需求是则采用ReentrantLock,这些需求包括:可定时的、可轮询的、可中断的以及考虑公平性的锁获取操作,还有非块结构的加锁方式;否则,还是应该优先使用synchronized
-因为,内置锁的优势在于:1. 被更多开发人员所熟悉,简洁紧凑,许多现有的程序中都已经使用了内置锁,如果将两种锁机制混用,容易发生错误并且可读性也不好;2. ReentrantLock的危险性比内置锁要高,如果忘记在finally块中调用unlock,那么相当于埋下了定时炸弹,很可能伤及其他代码;3. 未来更可能提升内置锁的性能,因为synchronized是JVM层面的内置属性,能够执行以一些优化; 4. 内置锁获取锁的操作能够与特定的栈帧关联起来,ReentrantLock 不可以。
1.5 读-写锁(ReadWriteLock接口 ReentrantReadWriteLock实现类)
- 在哪种情况下可以使用读/写锁:
1、一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行;
2、锁的持有时间较长(这种情况下多个读线程等待锁的时间就会越长,而读/写锁*支持多个读线程可以同时访问,省去了等待时间)
3、在实际情况中,对于在多处理器系统上被频繁读取的数据结构,读-写锁能够提升性能,实现更高的并发性。而在其他情况下,读-写锁的性能比读-占锁略差,因为复杂性更高。 - ReadWriteLock有多种实现方式(表现在读取锁与写入锁之间的交互方式):
1、释放优先:当一个写入操作释放写入锁时,等待队列中同时存在读线程和写线程,那么优先选择读线程、写线程,还是最先发出请求的线程?
2、读线程插队:如果锁是由读线程持有(注意没释放),但有写线程正在等待,那么新到达的读线程能否立即获得访问权?还是在写线程后面等待?如果允许读线程插队,那么将提高并发性,不过也可能造成写线程饥饿问题、
3、重入性:读取锁和写入锁是否是可重入的;关于可重入锁的介绍:当前线程进入某个同步方法已进获得了某个监视器对象所持有的锁,那么该线程在该方法中调用另一个同步方法也同样持有该锁。
4、降级:如果一个线程持有写入锁,那么它能否在不释放该锁的情况下获得读取锁?由写入锁降级为读取锁,同时不允许其他写线程修改被保护的资源
5、升级:读取锁能否优先于其他正在等待的读线程和写线程而升级成为一个写入锁? 大多数的读-写锁并不支持升级,因为容易造成死锁(如果两个读线程试图同时升级为写入锁,那么二者都不会释放读取锁(也就是没法结束读操作)) - ReentrantReadWriteLock:在java5.0中,读取锁的行为类似于一个信号量而不是锁,只是维护读线程的数量。在java6.0中修改了此行为:记录的是哪些线程已经获得了读取锁。
Java并发编程实战——显示锁相关推荐
- Java并发编程实战之互斥锁
文章目录 Java并发编程实战之互斥锁 如何解决原子性问题? 锁模型 Java synchronized 关键字 Java synchronized 关键字 只能解决原子性问题? 如何正确使用Java ...
- Java并发编程实战笔记2:对象的组合
设计线程安全的类 在设计现车让安全类的过程之中,需要包含以下三步: 找出构成对象状态的所有变量 找出约束状态变量的不变性条件 建立对象状态的并发访问策略 实例封闭 通过封闭机制与合适的加锁策略结合起来 ...
- aqs clh java_【Java并发编程实战】—– AQS(四):CLH同步队列
在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形. 其主要从双方面进行了改造:节点的结构与节点等待机制.在结构上引入了 ...
- Java并发编程实战_不愧是领军人物!这种等级的“Java并发编程宝典”谁能撰写?...
前言 大家都知道并发编程技术就是在同一个处理器上同时的去处理多个任务,充分的利用到处理器的每个核心,最大化的发挥处理器的峰值性能,这样就可以避免我们因为性能而产生的一些问题. 大厂的核心负载肯定是非常 ...
- java并发编程实战学习(3)--基础构建模块
转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put ...
- java单线程共享,「Java并发编程实战」之对象的共享
前言 本系列博客是对<Java并发编程实战>的一点总结,本篇主要讲解以下几个内容,内容会比较枯燥.可能大家看标题不能能直观的感受出到底什么意思,这就是专业术语,哈哈,解释下,术语(term ...
- JAVA并发编程实战-任务执行
目录 思维导图 1 在线程中执行任务 1.1 顺序执行任务 1.2 显式的为任务创建线程 1.3 无限制创建线程的缺点 2 Executor框架 2.1 使用Executor实现WebServer 2 ...
- 视频教程-Java并发编程实战-Java
Java并发编程实战 2018年以超过十倍的年业绩增长速度,从中高端IT技术在线教育行业中脱颖而出,成为在线教育领域一匹令人瞩目的黑马.咕泡学院以教学培养.职业规划为核心,旨在帮助学员提升技术技能,加 ...
- java并发编程实战(二)
java并发编程中常常会用到两种容器来存放一些数据,这些数据需要保证能在多线程下正常访问.常见的容器分为两类:同步容器和并发容器.在java并发编程实战一书中的第五章也有讲解. 什么是同步容器以及优劣 ...
最新文章
- 【数据结构作业心得】4-0 二叉树
- Android的Activity生命周期模拟程序及解析
- Python基础教程:super()方法详解
- Objective-c之NSCopying
- 微软发布VS Code Jupyter插件!不止Python!多语言的Jupyter Notebook支持来了!
- 通过Main的Checkpoint Restore加快Java启动速度
- 阿里云交通数据中台解决方案打造“数字化生产力”
- seata分布式事务一致性锁机制如何实现的
- 操作系统--进程管理1--单个CPU情况
- python字符串转义表
- Rust: codewars的Highest and Lowest
- 除了大家知道的navicat,再介绍两款免费的数据库连接工具
- TerraSolid工具试用系列2----TerraScan点云滤波(从点云中提取地面点)备注
- android 多个基站信息吗,Android模拟器模拟基站信息,并将wifi伪造成4g数据信息
- 群晖挂载阿里网盘通过 cloud sync 实现加密备份
- 虚拟机建Mac系统步骤
- [转]熊绎:我看软件工程师的职业规划
- 北京同创蓝天的全景航拍技术如何?应用在哪些方面呢?
- Word2019如何取消保存文件位置默认为Onedrive-个人——文档
- QWebEngine集成Netron可视化模型
热门文章
- 程序猿之华丽转身:必杀之八技
- 用python对股票进行可视化分析_使用Python对股票进行可视化分析
- NCE(Noise Contrastive Estimation) 与negative sampling
- Phoenix(凤凰)安装部署及介绍
- Android Studio第4部分:调试工具和生产力插件
- ie下载文件不弹出对话框
- 华为文化:任正非的“管理”门道
- 系统工程 软件工程(第三版)SCAU
- 解决报错“Ignoring ffi-1.12.2 because its extensions are not built. Try: gem pristine ffi --version 1.12”
- Java日历的制作(输入年月输出日历表)