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 ...
随机推荐
- 手把手教你 通过 NuGet.Server 包 搭建nuget服务器,并使用桌面工具上传 nuget 包,免命令行
新建web项目 工具:VS2013 版本:.Net Framework 4.6,低版本也行,不过要找到对应版本的Nuget.Server 装了NuGet客户端(百度如何安装) WebForm或MVC都 ...
- (十五)golang--init函数
每一个源文件都会包含一个init函数,这个函数会在执行main()执行之前被调用,相当于python中的__init__ 注意细节:(1)如果一个文件同时包含全局变量定义,init函数和main函数, ...
- 使用thanos管理Prometheus持久化数据
关于thanos的介绍可以参考这篇官方博客的翻译文档,本文不作部署操作介绍.下图是thanos的官方架构图,主要有5个组件: Query:可以近似看作是Prometheus的实现,用于采集其他组件的数 ...
- Spring Boot入门及第一个案例
一:SpringBoot是什么 springboot是对spring的缺点进行改善和优化,约定大于配置 开箱即用 没有代码生成 也无需xml 文件配置 可以修改属性值来满足需求 1) Spri ...
- Java 常用知识点汇总(数据类型之间转换、字符串的相关操作-截取、转换大小写等)
1.Java四类八种数据类型 byte:Java中最小的数据类型,在内存中占8位(bit),即1个字节,取值范围-128~127,默认值0 short:短整型,在内存中占16位,即2个字节,取值范围- ...
- Python 学习 第14篇:数据类型(元组和集合)
元组和集合是Python中的基本类型 一,元组 元组(tuple)由小括号.逗号和数据对象构成的集合,各个项通过逗号隔开,元组的特点是: 元组项可以是任何数据类型,也可以嵌套 元组是一个位置有序的对象 ...
- 获取Url地址中参数的3种方法【华为云技术分享】
获取Url的代码如下:window.location.href; 方法一:原生js(假设已经获得了Url地址) var url = 'https://gitbook.cn/gitchat/geekbo ...
- Asp.Net页面刷新防止跳转到其他浏览器或新的选项卡
前端页面js代码: <head> <script> window.name = "PremaritalCheckup_ManSocietyAgreeForm" ...
- MySql 获取数据库的所有表名
目录 写在前面 根据数据库获取该数据库下所有的表名 根据表名获取列名与列值 写在前面 在实现某个功能的时候,需要使用MySql数据库获取某数据的所有的表名以及该表名的所有列名与列值. 根据数据库获取该 ...
- chrome(谷歌)浏览器字体发虚解决办法
chrome(谷歌浏览器)浏览网页时,字体发虚的解决办法: 1.点击chrome里的 “设置” - 外观 - 字体,改为 微软雅黑,该方法测试无效. 2.将系统字体的pingfang字体卸载,完美解决 ...