java多线程核心技术——第四章总结
第一节使用ReentrantLock类
- 1.1使用ReentrantLock实现同步:测试1
- 1.2使用ReentrantLock实现同步:测试2
- 1.3使用Condition实现等待/同步错误用法与解决
- 1.4正确使用Condition实现等待/通知
- 1.5使用多个Condition实现通知部分线程:错误用法
- 1.6使用多个Condition实现通知部分线程:正确用法
- 1.7实现生产者/消费者模式:一对一交替打印
- 1.8实现生产者/消费者模式:多对多打印
- 1.9公平锁与非公平锁
- 1.10方法getHoldCount()、getQueueLength()和getWaitQueueLength()的测试
- 1.11方法hasQueuedThread()、hasQueuedThreads()、hasWaiters()的测试
- 1.12方法isFair()、isHeldByCurrentThread()和isLocked()的测试
- 1.13方法lockInterruptibly()、tryLock()和tryLock(Long timeout, TimeUnit unit)的测试
- 1.14方法awaitUninterruptibly()的使用
- 1.15方法awaitUntil()的使用
- 1.16使用Condition实现顺序执行
1.1使用ReentrantLock实现同步:测试1
使用下面代码获取ReenTrantLock对象lock
private Lock lock = new ReentrantLock();
调用lock.lock()方法可以对代码进行加锁,调用lock.unlock()方法对代码进行解锁。
注:需要进行加锁的代码放在lock()与unlock()方法中。
1.2使用ReentrantLock实现同步:测试2
调用了lock()方法代码的线程会持有“对象监视器”,其他线程只有等待锁被释放时再次争抢,效果跟使用synchronized关键字一样,线程间还是顺序执行的。
1.3使用Condition实现等待/同步错误用法与解决
类ReentrantLock实现等待/同步功能,需要借助于condition对象。
Condition类具有很好的灵活性,可以实现多路通知功能:在一个Lock对象中创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而有选择性地进行线程通知,在调度线程上更灵活。
在使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的,但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是常用,且在Condition中是默认的。
通过下面代码获取Condition实例
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
然后使用condition.await()来进行等待操作。
注:在进行await()前,必须调用lock.lock()来获取监视器对象,否则的话就会爆出异常:java.lang.IllegalMonitorStateException(无监视器异常)。
1.4正确使用Condition实现等待/通知
Object类中的wait()方法相当于Condition类中的await()方法。
Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。
Object类中的notify()方法相当于Condition类中的single()方法。
Object类中的notifyAll()方法相当于Condition类中的singleAll()方法。
1.5使用多个Condition实现通知部分线程:错误用法
一个Condition对象可以通过singleAll()方法唤醒所有调用lock.newCondition().await()的线程。
但是若想实现只唤醒部分线程,只能使用多个Condition对象了,这也就是Condition对象可以唤醒部分指定线程,提高程序运行效率。
1.6使用多个Condition实现通知部分线程:正确用法
若想实现Condition对象唤醒部分指定线程。一定要是调用等待的Condition对象与调用唤醒的Condition保持是一个Condition对象。
1.7实现生产者/消费者模式:一对一交替打印
public class Service {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean flag = false;
public void set() {
try {
lock.lock();
while (flag == true) {
condition.await();
}
System.out.println("我是set");
flag = true;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (flag == false) {
condition.await();
}
System.out.println("我是get");
flag = false;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
一个线程调用set(),一个线程调用get()就可以实现一对一交替打印。
1.8实现生产者/消费者模式:多对多打印
service代码还是同1.7,执行时开启多条线程即可,但是会遇到“假死”问题,可以通过将signal()改为signalAll()来解决。
1.9公平锁与非公平锁
锁Lock分为“公平锁”与“非公平锁”。
公平锁:标识线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。
非公平锁:一种线程抢占机制,是随机获取锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方法可能会导致某些线程一直拿不到锁,自然不公平。
创建的方式就是:
private Lock lock = new ReentrantLock(Boolean flag);
flag为true就是公平锁,flag为false就是非公平锁。
1.10方法getHoldCount()、getQueueLength()和getWaitQueueLength()的测试
1)int getHoldCount() 查询当前线程保持此锁定的个数,也就是调用lock()方法的次数。
2)int getQueueLength() 返回正等待获取此锁定线程估计数。
比如有5个线程,一个线程先执行await()方法,那么在调用getQueueLength()方法后返回值是4,说明有4个线程同时在等待lock的释放。
3)int getWaitQueueLength(Condition condition) 返回等待与此锁定相关的给定条件Condition的线程估计数。
比如有5个线程,每个线程都执行了同一个condition对象的await()方法,则调用getWaitQueueLength()返回的值就是5。
注:以上都是ReentrantLock类的API
1.11方法hasQueuedThread()、hasQueuedThreads()、hasWaiters()的测试
1)boolean hasQueuedThread(Thread thread) 查询指定的线程是否正在等待获取此锁定。
2)boolean hasQueuedThreads() 查询是否有线程正在等待获取此锁定。
3)boolean hasWaiters(Condition condition) 查询是否有线程正在等待与此锁定有关的condition条件。
注:以上都是ReentrantLock类的API
1.12方法isFair()、isHeldByCurrentThread()和isLocked()的测试
1)boolean isFair() 判断是不是公平锁
2)boolean isHeldByCurrentThread() 查询当前线程是否保持此锁定
3)boolean isLocked() 查询此锁定是否由任意线程保持
注:以上都是ReentrantLock类的API
1.13方法lockInterruptibly()、tryLock()和tryLock(Long timeout, TimeUnit unit)的测试
1)void lockInterruptibly() 如果当前线程未被中断(不处于中断状态),则获得锁定,如果已经被检测中断(处于中断状态)则出现异常。
爆出java.lang.InterruptedException异常。
2)boolean tryLcok() 仅在调用时锁定未被另一个线程保持的情况下,才获得该锁定。
3)boolean tryLock(long timeout, TimeUnit unit) 如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获得该锁定。
注:以上都是ReentrantLock类的API
1.14方法awaitUninterruptibly()的使用
此方法为condition的方法await()的替代。可以避免在挂起的时候遇到打断状态(interrupt)而爆出异常。
1.15方法awaitUntil()的使用
在指定时间后唤醒自己。参数为long
在线程等待期间也可以被其他线程唤醒。
此方法是condition的API
1.16使用Condition实现顺序执行
就是利用多个Condition实现顺序执行。
public class Main {
volatile private static int flag = 1;
private static ReentrantLock lock = new ReentrantLock();
final private static Condition c1 = lock.newCondition();
final private static Condition c2 = lock.newCondition();
final private static Condition c3 = lock.newCondition();
public static void main(String[] args) throws Exception{
Thread threadA = new Thread() {
@Override
public void run() {
try {
lock.lock();
while (flag != 1) {
c1.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadA" + (i + 1));
}
flag = 2;
c2.signalAll();
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
Thread threadB = new Thread() {
@Override
public void run() {
try {
lock.lock();
while (flag != 2) {
c2.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadB" + (i + 1));
}
flag = 3;
c3.signalAll();
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
Thread threadC = new Thread() {
@Override
public void run() {
try {
lock.lock();
while (flag != 3) {
c3.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadC" + (i + 1));
}
flag = 1;
c1.signalAll();
}catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
Thread[] a = new Thread[5];
Thread[] b = new Thread[5];
Thread[] c = new Thread[5];
for (int i = 0; i < 5; i++) {
a[i] = new Thread(threadA);
b[i] = new Thread(threadB);
c[i] = new Thread(threadC);
a[i].start();
b[i].start();
c[i].start();
}
}
}
第二节使用ReentrantReadWriteLock类
实际上ReentrantLock虽然能够保证实例变量的线程安全性,但是效率比较低。
为了在一些不需要操作实例变量的方法中,使用一些效率较高的锁来实现提升运行效率,即使用ReentrantReadWriteLock读写锁。
读写锁表示有两个锁,一个是读操作相关的锁,也成为共享锁;
另一个是写操作相关的锁,也叫排他锁。
也就是说读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。在没有线程进行写操作时,多个进行读操作的线程都可以获取读锁,而进行写入操作的Thread只有在获取写锁后才能进
行写入操作。
注:多个线程可以同时进行读操作,但同一时刻只允许一个Thread进行写入操作。
2.1读读共享
可以通过如下代码获取读写锁
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock()
就可以获得读锁,并进行加锁。
lock.readLock().unlock()
释放读锁。
注:读锁是共享锁。
2.2写写互斥
lock.writeLock().lock()
就可以获得写锁,并进行加锁。
lock.writeLock().unlock()
释放写锁。
注:写锁是互斥锁。
2.3读写互斥
2.4写读互斥
读锁与写锁是互斥的,也就是说不管是读锁先加锁还是写锁先加锁,只要两者相遇都是互斥的,必须等待其中一个释放线程后才能够再获得锁。
“读写”、“写读”、“写写”都是互斥的(因为写锁时互斥锁,所以只要出现写锁,就一定是互斥的)
“读读”是共享的,非互斥的。
本章总结:
本章用Lock来替换掉了synchronized关键字。
Lock很重要,重要到什么程度?很多并发包的源码都是通过Lock实现的。学好了Lock,有助于你理解你进一步进阶去看一些并发的源码。
本文内容是书中内容兼具自己的个人看法所成。可能在个人看法上会有诸多问题(毕竟知识量有限,导致认知也有限),如果读者觉得有问题请大胆提出,我们可以相互交流、相互学习,欢迎你们的到来,心成意足,等待您的评价。
java多线程核心技术——第四章总结的更多相关文章
- “全栈2019”Java多线程第二十四章:等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第十四章:线程与堆栈详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Java多线程核心技术(四)Lock的使用
本文主要介绍使用Java5中Lock对象也能实现同步的效果,而且在使用上更加方便. 本文着重掌握如下2个知识点: ReentrantLock 类的使用. ReentrantReadWriteLock ...
- 从ConcurrentHashMap的演进看Java多线程核心技术 Java进阶(六)
本文分析了HashMap的实现原理,以及resize可能引起死循环和Fast-fail等线程不安全行为.同时结合源码从数据结构,寻址方式,同步方式,计算size等角度分析了JDK 1.7和JDK 1. ...
- Java多线程学习(四)等待/通知(wait/notify)机制
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79690279 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- Java多线程核心技术(六)线程组与线程异常
本文应注重掌握如下知识点: 线程组的使用 如何切换线程状态 SimpleDataFormat 类与多线程的解决办法 如何处理线程的异常 1.线程的状态 线程对象在不同运行时期有不同的状态,状态信息就处 ...
- Java多线程核心技术(五)单例模式与多线程
本文只需要考虑一件事:如何使单例模式遇到多线程是安全的.正确的 1.立即加载 / "饿汉模式" 什么是立即加载?立即加载就是使用类的时候已经将对象创建完毕,常见的实现办法就是直接 ...
- “全栈2019”Java多线程第三十三章:await与signal/signalAll
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第三十七章:如何让等待的线程无法被中断
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
随机推荐
- IdentityServer4 禁用 Consent screen page(权限确认页面)
IdentityServer4 在登录完成的适合,会再跳转一次页面(权限确认),如下: 我之前以为 IdentityServer4 就是这样使用的,但实际业务场景并不需要进行权限确认,而是登陆成功后直 ...
- (11.06)Java小知识
最近由于课程变化,学习计划也跟着改动,留给我写博客的时间也越来越少.今天晚上没有课,抽空过来图书馆写一写,许久不写感觉都有点陌生了! 今天要和大季家分享的衔接了上一篇博客,是关于方法的嵌套调用与递归调 ...
- 使用js编写一个简单的运动框架
下班后,,没事捣鼓捣鼓个人的小爱好. 首先,说明我的这个运动框架(css所有属性)也是常见的框架一种,健壮性并不是太好,对于新手学习倒是挺好,,若是大神,老司机请拐弯. 上来,我们先定义一个区块,然后 ...
- 检测CSS属性 是否支持
原理是:创建一个节点,判断其的style属性是否含有textOverflow属性,有则进一步判断是否支持ellipsis这个值.当遇到不支持的属性值时,浏览器会直接把这个值抛弃.因此这里就可以先给te ...
- android视频播放器系列(一)——系统播放器
使用系统播放器(intent隐士调用)可以播放本地视频和网络视频,但是使用方式上稍微有点差别: 一.播放本地视频 Uri uri = Uri.parse("本地视频地址");Int ...
- word,excel,ppt转Pdf,Pdf转Swf,通过flexpaper+swftools实现在线预览
其实这是我好几年前的项目,现在再用这种方式我也不建议了,毕竟未来flash慢慢会淘汰,此方式也是因为目测大部分人都装了flash,才这么做的,但是页面展示效果也不好.其实还是考虑收费的控件,毕竟收费的 ...
- [转载] Java NIO教程
转载自并发编程网 – ifeve.com http://ifeve.com/java-nio-all/ 关于通道(Channels).缓冲区(Buffers).选择器(Selectors)的故事. 从 ...
- WebService学习总结
因为最近开发的项目需求中涉及到了webservice,正好对这块知识以前学过但是又忘记了,于是想着从新学习下,整理一个笔记,便于后面的复习.于是有了本文,下面开始介绍webservice. 一.简介 ...
- Mysql 用法
一转眼,一个星期过去了,来到测试班也一个星期了,今天经历了一次,这是自己这一周的总结,也算对自己这一周的一个交代. 几个比较重要的语句: 查看数据库 show databases; 创建数据库 cre ...
- ecshop添加菜单以及权限分配
增加菜单并添加权限: admin/includes/inc_menu.php 添加 $modules['**_name1']['name2'] = 'your href' admin/includes ...