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 ...
随机推荐
- 在 Vue 中使用 装饰器 Decorator
Decorator 的语法还没有通过提案,所以项目中很少用.不过最近刚好有一个需求用到了. 装饰器的语法 http://es6.ruanyifeng.com/#docs/decorator 需求是,有 ...
- Java实现牛顿迭代法求解平方根、立方根
一.简介 牛顿迭代法(Newton's method)又称为牛顿-拉夫逊(拉弗森)方法(Newton-Raphson method),它是牛顿在17世纪提出的一种在实数域和复数域上近似求解方程的方法. ...
- Nacos配置的多环境管理
实现多环境管理有下面几种方式 1.使用Data ID与profiles实现 在 Nacos Config Starter 中,dataId 的拼接格式为 ${prefix} - ${spring.pr ...
- SpringBoot第十八篇:异步任务
作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/11095891.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言 系统中的异 ...
- LeetCode 167:两数之和 II - 输入有序数组 Two Sum II - Input array is sorted
公众号: 爱写bug(ID:icodebugs) 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数. 函数应该返回这两个下标值 index1 和 index2,其中 index ...
- Angular6 学习笔记——路由详解
angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...
- SQL --------------- 运算符 = 与 in
in 用于指定查询与where 一块进行使用,可以用来指定一个或多个,和 “ = ” 差不多 语法: select * from 表名 where 字段 in (字段对应的值可以是一个或多个) 建个表 ...
- Redmine添加插件
注意:插件的安装必须到Plugins对应的文件目录执行命令! Plugins Plugin list A full list of available Redmine plugins can be f ...
- Sitecore 十大优秀功能
为客户的需求创建最佳解决方案是我们的主要目标.良好的设计不仅仅是视觉吸引力,还要确保用户体验简单直观.在设计Sitecore网站时,我们始终牢记这一点 . 以下是一些我最喜欢的功能,可以帮助我们使用 ...
- Spring Boot 运行原理 - 核心注解
https://www.jianshu.com/p/23f504713b94 核心注解 打开上面任意一个AutoConfiguration文件,一般都有下面的条件注解,在spring-boot-aut ...