Java多线程编程核心技术-第4章-Lock的使用-读书笔记
第 4 章 Lock 的使用
本章主要内容
ReentrantLocal 类的使用。
ReentrantReadWriteLock 类的使用。
4.1 使用 ReentrantLock 类
在 Java 多线程中,可以使用 synchronized 关键字来实现线程之间同步互斥,但在 JDK 1.5 中新增加了 ReentrantLock 类也是达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比 synchronized 更加的灵活。
4.1.1 使用 ReentrantLock 实现同步:测试 1
调用 ReentrantLock 对象的 lock() 方法获得锁,调用 unlock() 方法释放锁。
当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印。线程打印的数据是分组打印(一个线程打印的数据在一块),因为当前线程已经持有锁,但线程之间打印的顺序是随机的。
4.1.2 使用 ReentrantLock 实现同步:测试 2
调用 lock.lock() 代码的线程就持有了“对象监视器”,其他线程只有等待锁被释放时再次争抢。效果和使用 synchronized 关键字一样,线程之间还是顺序执行的。
4.1.3 使用 Condition 实现等待 / 通知:错误用法与解决
关键字 synchronized 与 wait() 和 notify()/notifyAll() 方法相结合可以实现等待 / 通知模式,类 ReentrantLock 也可以实现同样的功能,但需要借助于 Condition 对象。Condition 类是 JDK5 中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个 Lock 对象里面可以创建多个 Condition(即对象监视器)实例,线程对象可以注册在指定的 Condition 中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。
在使用 notify()/notifyAll() 方法进行通知时,被通知的线程却是由 JVM 随机选择的。但是用 ReentrantLock 结合 Condition 类是可以实现“选择性通知”,这个功能是非常重要的,而且在 Condition 类中是默认提供的。
而 synchronized 就相当于整个 Lock 对象中只有一个单一的 Condition 对象,所有的线程都注册在它一个对象的身上。线程开始 notifyAll() 时,需要通知所有的 WAITING 线程,没有选择权,会出现相当大的效率问题。
在 condition.wait() 方法调用之前调用 lock.lock() 代码获得同步监视器,不然就会报 java.lang.IllegalMonitorStateException 异常。
调用 Condition 对象的 await() 方法,使当前执行任务的线程进入了等待 WAITING 状态。
4.1.4 正确使用 Condition 实现等待 / 通知
Object 类中的 wait() 方法相当于 Condition 类中的 await() 方法。
Object 类中的 wait(long timeout) 方法相当于 Condition 类中的 await(long time,TimeUnit unit) 方法。
Object 类中的 notify() 方法相当于 Condition 类中的 signal() 方法。
Object 类中的 notifyAll() 方法相当于 Ccondition 类中的 signalAll() 方法。
4.1.5 使用多个 Condition 实现通知部分线程:错误用法
使用一个 Condition 对象,多个方法等待,当调用 signalAll() 方法唤醒等待线程时,所有的等待线程都会被唤醒。
如果想要单独唤醒部分线程该怎么处理呢?这时就有必要使用多个 Condition 对象了,也就是 Condition 对象可以唤醒部分指定线程,有助于提升程序运行的效率。可以先对线程进行分组,然后再唤醒指定组中的线程。
4.1.6 使用多个 Condition 实现通知部分线程:正确用法
使用多个 Condition 对象,可以实现通知部分线程。
使用 ReentrantLock 对象可以唤醒指定种类的线程,这是控制部分线程行为的方便方式。
4.1.7 实现生产者 / 消费者模式:一对一交替打印
通过使用 Condition 对象,成功实现交替打印的效果。
4.1.8 实现生产者 / 消费者模式:多对多交替打印
使用多个线程来交替打印,如果使用 signal() 方法就会出现假死,要使用 signalAll() 方法来解决。
4.1.9 公平锁与非公平锁
公平与非公平锁:锁 Lock 分为 “公平锁” 和 “非公平锁”,公平锁表示线程获得锁的顺序是按照线程加锁的顺序来分配的,即先来先得的 FIO 先进先出顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。
new ReentrantLock(boolean isFair) 传递参数 isFair 表示是否是公平锁。公平锁的结果是基本程序有序的状态,不保证完全有序。
4.1.10 方法 getHoldCount()、getQueueLength() 和 getWaitQueueLength() 的测试
1)方法 int getHoldCount() 的作用是查询当前线程保持此锁定的个数,也就是调用 lock() 方法的次数。
2)方法 int getQueueLength() 的作用是返回正等待获取此锁定的线程估计数。比如有 5 个线程,1 个线程首先执行 await() 方法,那么在调用 getQueueLength() 方法后返回值是 4,说明有 4 个线程同时再等待 lock 的释放。
3)方法 int getWaitQueueLength(Condition condition) 的作用是返回等待与此锁定相关的给定条件 Condition 的线程估计数,比如 5 个线程,每个线程都执行了同一个 condition 对象的 await() 方法,则调用 getWaitQueueLength(Condition condition) 方法时返回的 int 值是 5 。
4.1.11 方法 hasQueuedThread()、hasQueuedThreads() 和 hasWaiters() 的测试
1)方法 boolean hasQueuedThread(Thread thread) 的作用是查询指定的线程是否正在等待获取此锁定。
方法 boolean hasQueuedThreads() 的作用是查询是否有线程正在等待获取此锁定。
2)方法 boolean hasWaiters(Condition condition) 的作用是查询是否有线程正在等待与此锁定有关的 condition 条件。
4.1.12 方法 isFair()、isHeldByCurrentThread() 和 isLocked() 的测试
1)方法 boolean isFair() 的作用是判断是不是公平锁。
在默认的情况下,ReentrantLock 类使用的是非公平锁。
2)方法 boolean isHeldByCurrentThread() 的作用是查询当前线程是否保持此锁定。
3)方法 boolean isLocked() 的作用是查询此锁定是否由任意线程保持。
4.1.13 方法 lockInterruptibly()、tryLock() 和 tryLock(long timeout,TimeUnit unit) 的测试
1)方法 void lockInterruptibly() 的作用是:如果当前线程未被中断,则获取锁定,如果已经被中断则出现异常。
当调用 lock.lockInterruptibly() 方法时,调用 thread.interrupt() 方法是会报异常。
2)方法 boolean tryLock() 的作用是,仅在调用时锁定未被另一个线程保持的情况下,才获得该锁定。
3)方法 boolean tryLock(long timeout,TimeUnit unit) 的作用是,如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
4.1.14 方法 awitUniterruptibly() 的使用
当调用了thread.interrupt()方法之后,condition.await() 方法会报异常,但是调用 awaitUniterruptibly() 方法不会报异常。
4.1.15 方法 awaitUntil() 的使用
调用 awaitUntil(long time) 是等待 time 时间后自动唤醒。
4.1.16 使用 Condition 实现顺序执行
使用 Condition 对象可以对线程执行的业务进行排序规划。
使用 Condition 对线程进行分类,然后进行分类唤醒,从而实现排序规划。
4.2 使用 ReentrantReadWriteLock 类
类 ReentrantLock 具有完全互斥排他的效果,即同一时间只有一个线程在执行 ReentrantLock.lock() 方法后面的任务。这样做虽然保证了实例变量的线程安全性,但效率却是非常低下的。所以在 JDK 中提供了一种读写锁 ReentrantReadWriteLock 类,使用它可以加快运行效率,在某些不需要操作实例变量的方法中,完全可以使用读写锁 ReentrantReadWriteLock 来提升钙方法的代码运行速度。
读写锁表示也有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排他锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。在没有线程 Thread 进行写入操作时,进行读取操作的多个 Thread 都可以获取读锁,而进入写入操作的 Thread 只有在获取写锁后才能进行写入操作。即多个 Thread 可以同时进行读取操作,但是同一时刻只允许一个 Thread 进行写入操作。
4.2.1 类 ReentrantReadWriteLock 的使用:读读共享
使用 lock.readLock() 读锁可以提高程序运行效率,允许多个线程同时执行 lock() 方法后面的代码。两个线程几乎同时进入 lock() 方法后面的代码。
4.2.2 类 ReentrantReadWriteLock 的使用:写写互斥
使用写锁代码 lock.writeLock() 的效果就是同一时间只允许一个线程执行 lock() 方法后面的代码。
4.2.3 类 ReentrantReadWriteLock 的使用:读写互斥
“读写”操作是互斥的。
4.2.4 类 ReentrantReadWriteLock 的使用:写读互斥
“写读”操作也是互斥的。即只要出现 “写操作” 的过程,就是互斥的。
“读写”、“写读” 和 “写写” 都是互斥的;而 “读读” 是异步的,非互斥的。
4.3 本章总结
在学习并发时,Lock 是 synchronized 关键字的进阶,掌握 Lock 有助于学习并发包中源代码的实现原理,在并发包中大量的类使用了 Lock 接口作为同步的处理方式。
Java多线程编程核心技术-第4章-Lock的使用-读书笔记的更多相关文章
- Java多线程编程核心技术-第3章-线程间通信-读书笔记
第 3 章 线程间通信 线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大 ...
- Java多线程编程核心技术-第1章-Java多线程技能-读书笔记
第 1 章 Java 多线程技能 本章主要内容 线程的启动 如何使线程暂停 如何使线程停止 线程的优先级 线程安全相关的问题 1.1 进程和多线程的概念及线程的优点 进程是操作系统结构的基础:是一次程 ...
- Java多线程编程核心技术-第2章-对象及变量的并发访问-读书笔记
第 2 章 对象及变量的并发访问 本章主要内容 synchronized 对象监视器为 Object 时的使用. synchronized 对象监视器为 Class 时的使用. 非线程安全是如何出现的 ...
- java多线程编程核心技术——第四章总结
第一节使用ReentrantLock类 1.1使用ReentrantLock实现同步:测试1 1.2使用ReentrantLock实现同步:测试2 1.3使用Condition实现等待/同步错误用法与 ...
- Java多线程编程核心技术-第7章-拾遗增补-读书笔记
第 7 章 拾遗增补 本章主要内容 线程组的使用. 如何切换线程状态. SimpleDataFormat 类与多线程的解决办法. 如何处理线程的异常. 7.1 线程的状态 线程对象在不同的运行时期有不 ...
- Java多线程编程核心技术-第5章-定时器 Timer-读书笔记
第 5 章 定时器 Timer 定时 / 计划功能在移动开发领域使用较多,比如 Android 技术.定时计划任务功能在 Java 中主要使用的就是 Timer 对象,他在内部使用多线程的方式进行处理 ...
- java多线程编程核心技术——第三章
第一节等待/通知机制 1.1不使用等待/通知机制实现线程间的通讯 1.2什么是等待/通知机制 1.3等待/通知机制的实现 1.4方法wait()锁释放与notify()锁不释放 1.5当interru ...
- java多线程编程核心技术——第七章补漏拾遗
本章主要知识点: 1)线程组的使用 2)如何切换线程状态 3)SimpleDateFormat类与多线程的解决方法 4)如何处理线程异常. 这本书基本来到了终点,其实在第四章来说,核心(基础)的线程知 ...
- java多线程编程核心技术——第三章总结
第一节等待/通知机制 1.1不使用等待/通知机制实现线程间的通讯 1.2什么是等待/通知机制 1.3等待/通知机制的实现 1.4方法wait()锁释放与notify()锁不释放 1.5当interru ...
随机推荐
- Django-Debug-Toolbar插件
目录 django配置插件: 介绍: 安装及配置: 优化ORM: django配置插件: ---配置Django-Debug-Toolbar 介绍: Django-Debug-Toolbar是项目开发 ...
- [LeetCode] 37. Sudoku Solver 求解数独
Write a program to solve a Sudoku puzzle by filling the empty cells. A sudoku solution must satisfy ...
- 黑科技!两行代码完美解决:同时设置overflow-x:hidden,overflow-y:visible无效的问题
不废话,直接上代码 <!DOCTYPE html> <html> <head> <style> body { padding: 0; margin: 0 ...
- Elasticsearch由浅入深(十一)索引管理
索引的基本操作 创建索引 PUT /{index} { "settings": {}, "mappings": { "properties" ...
- 花一天时间踩了node npm的一个坑
在一个后端眼里nodejs这工具真的难用,最近为了用elementui,然后去硬着头皮学vue,学着学着,发现还要去用node,webpack.真想掐死前端那一群人啊.... 好了,进入正题.话说我装 ...
- 【网络知识之五】TCP
TCP协议:传输控制协议. 一.TCP保证可靠性的机制1.校验和 TCP报头有16位检验和: 由发送端填充, 检验形式有CRC校验等. 如果接收端校验不通过, 则认为数据有问题. 此处的校验和不光包含 ...
- python运维开发常用模块(一)psutil
1.模块简介 psutil是一个跨平台库(http://code.google.com/p/psutil/),能够轻 松实现获取系统运行的进程和系统利用率(包括CPU.内存.磁盘.网 络等)信息.它主 ...
- 论文阅读: Direct Monocular Odometry Using Points and Lines
Direct Monocular Odometry Using Points and Lines Abstract 大多数VO都用点: 特征匹配 / 基于像素intensity的直接法关联. 我们做了 ...
- Unity Shader 序列帧动画
shader中的序列帧动画属于纹理动画中的一种,主要原理是将给定的纹理进行等分,再根据时间的变化循环播放等分中的一部分. Unity Shader 内置时间变量 名称 类型 描述 _Time floa ...
- DirectX:Vector
Tag DirectX下的博客主要用于记录DirectX的学习过程,主要参考<DirectX 12 3D 游戏实战开发>. Vector in DirectX Shader的编写离不开数学 ...