并发实战
2021-05-02 13:05:06 12 举报
AI智能生成
并发实战
作者其他创作
大纲/内容
第二章 线程安全性
第零节
一、理解
1)理解前提术语
1、共享
①什么是状态共享
2、可变
①什么是状态可变
3、状态
①什么是状态变量
②有哪四种状态变量
2)理解线程安全性
1、出现线程安全问题
①本质原因
②如何判断线程是否安全
2、三种解决线程安全问题的思路
3、java中的几种同步机制
第一节 线程安全性
一、定义
1)复杂的定义
2)困惑的定义
1、这些定义让人困惑的两个原因
2、如何定义线程安全,才不会困惑
3)理解前提
1、理解『正确性』
①什么是『正确性』
3)正确的定义
1、简易版
2、完全版
二、举例
1)因数分解 servlet
1、功能说明
2、代码举例
3、此 servlet 线程安全的原因
4、无状态对象,线程安全的原因
①只有局部变量
A、如何理解局部变量,不会产生线程安全性问题
②没有共享变量
A、如何理解无状态对象,没有共享状态?
③为什么说,多个线程访问同一个无状态对象,就好像访问不同的实例一样,也即好像没有访问共享的对象一样
第二节 原子性
一、新的需求
二、解决方案
1)叙述
2)代码
3)可能的错误
1、起初
2、过程
3、最后
4、错误原因
三、竞态条件
1)概念
1、什么是『竞态条件』
①概念
②特点
③解决思路
2、什么时候会出现『竞态条件』
3、为什么竞态条件,会带来线程安全性问题
2)分类
1、『先检查后执行』类型竞态条件
①为什么『先检查后执行』类型是竞态条件中最常见的
②本质
③举例(没有做思维导图,今后做)
现实生活中『先检查后执行』类型的竞态条件
延迟初始化中的『先检查后执行』类型的竞态条件
2、『读取-修改-写入』类型竞态条件(没有做思维导图,今后做)
3)存在的问题
执行时序、竞态条件与线程安全的关系
4)解决方案
1、解决『竞态条件』引起的线程安全性问题的『思路』
①思路叙述
②思路解释
A、为什么,要解决『竞态条件』问题,就要解决执行时序不确定的问题?而不是把多线程变为单线程?
B、使用什么方法,解决执行时序不正确的问题?
C、什么是正确的执行时序?
D、如何确保正确的执行时序
2、复合操作
①严谨地描述复合操作
②什么是复合操作?
③如何定义复合操作
A、方法一:加锁机制(下一节讲)
B、方法二:使用一个现有的原子变量类
a、代码
b、解释
c、结论
第三节 加锁机制
一、原子变量类的局限性
1)当前的原子变量类,使用情况?
2)当前的原子变量类,存在的问题?
1、原子变量可以满足需要的情况
①对象只有一个状态变量
②对象有多个状态变量,但是状态变量之间,没有联系,也即不变性条件,不会由多个变量共同来描述
2、原子变量不可以满足需要的情况
①多个状态变量,共同描述不变性条件
3)局限性
二、带有缓存的因式分解servlet
1)目的
2)带有缓存的因式分解 servlet,线程不安全的实现方式
1、思路
2、代码
3、存在的问题
① servlet 是线程不安全的
②UnsafeCachingFactorizer的不变性条件之一
③解释:为什么不满足不变性条件
④错误结果
⑤解决方案
3)疑问
三、内置锁
1)java 如何实现原子性
2)同步代码块的两个部分
①第一个部分——作为锁的对象引用
②第二个部分——作为由这个锁保护的代码块
3)锁
①何时获得锁,何时释放锁?
A、获得锁
B、释放锁
②如何获得内置锁?
③锁的作用?
A、特点
B、举例解释
4)『带有缓存的因式分解 servlet』线程安全的实现方式
①思路
②特点:简单
③方法
④问题
⑤代码
四、重入
1)什么是重入?(内置锁是可重入的)
2)重入的一种实现方法
①变量
②过程
3)为什么需要『重入』这种功能?
①叙述
②代码
③解释
A、两个同步方法
B、同一把锁
C、什么情况下,线程会请求一个『已经由自己持有的』锁
D、为什么,如果不能重入,就会出现死锁?
第四节 用锁来保护状态(笔记为做好,思维导图暂时不做)
第五节 活跃性与性能
一、举什么例子说明『活跃性与性能』
1)第一阶段:不安全的 servlet
2)第二阶段:没有并发的 servlet
3)存在的问题
1、叙述
2、问题根本原因
3、可以实现功能——保证线程安全
4、代价很大
4)新的解决思路
1、叙述
2、注意事项
3、同步只需要加在什么地方?
5)新的代码
1、解释
①第一个同步代码块
②第二个同步代码块
③因数分解功能之外,引入了两个计数器
④同步块之外的代码
第三章 对象的共享
第零节
一、关键字 synchronized 的两个作用
1)保证原子性
2)保证内存可见性
二、何时需要确保原子性,内存可见性
什么时候需要保证『原子性』
什么时候需要保证『内存可见性』
三、锁、同步块、阻塞的关系
第一节 可见性
一、概念
1)单线程和多线程,对『内存写入操作』的『可见性』
1、在单线程环境中
2、在多线程环境中
二、举例 说明
1)叙述
2)代码
3)解释
1、共享变量
2、主线程和读线程的功能
3、可能的两种『不希望情况』
A、正确情况一
B、错误情况一
C、错误情况二
a、叙述
b、原因
c、解释——重排序的结果
d、什么是『重排序』
f、出现『重排序』的根本原因
D、几种结果的分析
①打印出0的原因
②主线程很长时间没有打印
三、失效数据
1)什么时候,会产生失效数据?
2)更糟糕的是,失效值可能不会同时出现
3)失效数据带来的错误后果?
4)非线程安全的可变整数类——Mutablelnteger
1、代码
2、不安全的原因
3、什么时候会出现『失效值问题』
5)线程安全的可变整数类——SynchronizedInteger
1、代码
2、解释
四、Volatile变量
1)如何确保可见性
1、含义
2、不重排序
3、不缓存(也即总会返回最新值)
2)volatile变量和sychronized同步块的区别
3)『volatile变量』和可见性的关系
1、volatile变量对可见性的影响比volatile变量本身更为重要
2、如何使用
3、作用
4、『volatile变量』类比同步代码块的在使用方法上的比较
5、为什么,不建议过度依赖volatile变量提供的可见性
10、『当且仅当』满足以下『所有条件』时,才应该使用『volatile变量』
①不需要『确保原子性』的情况下
A、在多个线程进行修改时,一定不能出现竞态条件
B、单个线程进行修改时(一定不会出现竞态条件,你不需要确保原子性)
②不需要使用更高级的技术
第二节 发布与逸出
一、概念
1)什么是发布(Publish)一个对象
①叙述
②举例
2)不同的需求
①有时需要『不发布对象』
②有时需要『发布对象』
③有时需要『发布对象,并确保对象的线程安全性』
3)为什么说,发布对象可能会破坏对象的『线程安全性』
①叙述
②举例
4)什么是逸出(Escape)
5)发布对象的几个方法
6)发布对象——举例说明
①代码
②解释
③结论
7)逸出——举例说明
①代码
②解释
③什么是 Alien 外部方法
A、叙述
B、包括
④为什么把一个对象,传递给某个外部方法时,就『相当于』发布了这个对象?
⑤为什么是『相当于』
⑥解决方案——如何才能不逸出
A、叙述
B、代码
C、解释
8)发布内部类实例,会连带发布其外围类实例
1、代码
2、解释
二、安全的对象构造过程(思维导图,缓一缓)
第三节 防止发布(线程封闭)
一、基本概念
1)实现『线程安全』的几个办法
1、同步
2、不共享:『线程封闭』
①叙述
②原因
③如何实现一个对象(数据的一种),只由单个线程独占
3、不可变
2)『同步』技术和『线程封闭』技术的比较
3)Java 语言及其核心库中,维持『线程封闭性』的一些机制
1、局部变量
2、ThreadLocal 类
4)线程封闭技术举例
1、例子叙述
2、如何在程序中,实现 Connection 对象的线程封闭
3、为什么JDBC规范,并不要求 Connection 对象必须是线程安全的
二、三种分类
1)Ad-hoc 线程封闭 (不是很懂,思维导图,暂时不做)
2)栈封闭
1、『栈封闭』与『线程封闭』的关系?
2、『栈封闭』的充分条件是什么
3、为什么说,『封闭在执行线程中』是『局部变量』的固有属性
4、『栈封闭』相较于『Adhoc线程封闭』的优点
5、代码举例
解释
6、为什么说,对于『基本类型的局部变量』无论如何都不会破坏『栈封闭性』
①叙述
②我的猜想
③举例
7、在『维持对象引用』的『栈封闭性』时,程序员『需要多做一些工作』以确保被『引用的对象不会逸出』
①当前『集合animals』的引用,『封闭在执行线程』中
②发布引用,对象逸出
③引用、对象的存储位置
3)ThreadLocal 类
1、原理
2、作用
3、举例说明
①单线程中的『数据库连接——Connection对象』
②多线程中的『数据库连接——Connection对象』
4、线程封闭的Connection对象,举例说明
①代码
②解释
6、如何『单线程应用程序』移植到『多线程环境』
7、在实现应用程序框架时,大量使用了ThreadLocal
8、开发人员经常滥用ThreadLocal
第四节 不变性
一、概念
1)带来『原子性』和『内存可见性』的根本原因
①此前介绍了许多,与原子性和可见性,相关的问题
②根本原因
2)一种解决思路
3)什么是『不可变对象』
4)为什么说,线程安全性是『不可变对象』的固有属性之一
5)不可变对象一定是线程安全的
6)为什么说,不可变对象很简单
7)为什么『不可变对象』相较于『可变对象』更加可信
8)定义不可变对象的两种方法
1、不可变对象内部,使用不可变对象来管理它们的状态(也即不可变对象嵌套,也即权威final)
2、不可变对象内部,使用可变对象来管理它们的状态,但是可变对象,不能对外发布
9)所有的域都声明为final类型,就是不可变性对象吗?
10)满足不可变对象的几个条件
①对象创建以后,其状态就不能修改
②对象的所有域,都是final类型
③对象是正确创建的(在对象的创建期间,this引用没有逸出)
11)在不可变对象的内部仍可以使用可变对象来管理它们的状态,但需要满足一定的条件
①代码
②解释:不清楚——为什么是这样
②条件
二、Final
1、final的作用?
2、什么时候使用『不可变对象』
3、即使对象是可变的,final也可以『限制对象的可变性』
4、良好的编程习惯
①『除非需要更高的可见性,否则应该将所有的域都声明为私有域』
②『除非需要某个域是可变的,否则应该将其声明为final域』
三、使用『Volatile类型』来发布『不可变对象』(思维导图,下次做)
四、定义不可变对象的两个方法
1、使用 final
2、不定义 setter方法
3、不定义 getter 或其它方法,返回状态的引用(如果状态是一个对象的情况下)
第五节 安全发布
一、
1、什么时候你不能发布对象
2、如何确保对象不被发布
3、什么情况下,你需要发布对象
4、不安全的发布
①代码
②疑惑
③原因
④解释
二、不正确的发布:正确的对象被破坏
1、什么是『未被正确发布』
2、在『未被正确发布的对象』中存在哪两个问题
3、以上三种情况,没有理解是什么回事!!
三、不可变对象与初始化安全性
四、安全发布的常用模式
第四章 对象的组合
第一节 设计线程安全的类
一、概述
1)设计线程安全类的三个基本要素
1、找出『构成对象状态』的『所有变量』
2、找出约束状态变量的『不变性条件』
3、建立『对象状态』的『并发访问』『管理策略』
2)如何分析『对象的状态』?
1、如果对象中所有的域都是『基本类型的变量』
①叙述
②举例
2、如果在对象的域中引用了其他对象
①叙述
②举例
3)什么是同步策略(Synchronization Policy)
1、说法一
2、说法二
4)为什么必须将『同步策略』写为『正式文档』
二、收集同步需求
1)什么是收集同步需求
2)不可变条件
1、在『并发访问的情况下』,为什么要防止,线程的『不变性条件』,遭到破坏
2、如何防止,类的『不变性条件』,遭到破坏
3、什么是『状态空间』
4、『状态空间』大小与『可能状态』分析难度
5、『不可变条件』的作用?
6、『不可变条件』与『状态空间』的举例说明:
A、例子
B、状态空间
C、不可变条件
D、关系
3)后验条件
①『后验条件』的作用?
②『后验条件』举例
③什么时候,不需要『后验条件』?
④举例说明——不需要『后验条件』的情况
三、如何满足同步需求
1)换一种说法
2)如何满足不变性条件
①叙述
②举例
3)如何满足后验条件
4)方式比较
①封装——》满足『不变性条件』——》避免产生『无效状态』
②同步——满足『后验条件』——》避免产生『无效的状态转换』
③说明
5)存在的问题
1、叙述
2、存在着什么特殊情况
3、举例
4、为什么『包含多个变量的不变性条件』将带来原子性需求
6)为什么『不变性条件』和『后验条件』都会带来原子性要求
①『不变性条件』可能带来原子性要求
②『后验条件』可能带来原子性要求
③举例说明:『后验条件』与『不变性条件』是不同的
A、差异叙述
B、共同合作
四、依赖状态的操作
1)『先验条件』
1、作用叙述
①『先验条件』
②『后验条件』
2、举例
3、对比
A、『先验条件』
B、『后验条件』
2)什么是『依赖状态的操作』
3)『单线程和多线程』中的『先验条件』
①在『单线程程序』中
②在『并发程序』中
③无论在『单线程程序』还是在『并发程序』中,都需要等到『先验条件』为真,才会执行该操作
4)如何实现『依赖状态的操作』?也即如何『实现』某个『等待先验条件为真时』才执行的『操作』
1、使用在『平台与类库中』提供的『各种底层机制』来『创建依赖状态的类』(第14章将介绍)
2、一种更简单的方法——利用『现有类库』:(第5章将介绍)
3、阻塞类是同步工具类的子集,阻塞类包括——阻塞队列(Blocking Queue)或信号量(Semaphore)
四、状态的所有权
1)概念
1、对象的状态,包括哪些
①叙述
②解释
A、什么是对象图——从对象图与类图的关系说起
a、不同点
b、相同点
0 条评论
下一页