《代码整洁之道》--第13章 并发
1. 为什么要并发
a) 并发是一种解耦策略。他帮助我们吧做什么(目的)和何时做(时机)分解开。
b) 在web应用的servlet模式下,当有web请求时,servlet就会异步执行。
2. 挑战
a) 当两个线程相互影响时就会出现不可预期的情况。这是因为线程在执行那行java代码时有许多可能路径可行,有些路径会产生错误的结果。回答这个问题需要理解 Just-IN-Time编译器如何对待生成的字节码,还
要理解java内存模型认为什么东西具有原子性
3. 并发防御原则
a) SRP
i. 并发相关的代码有自己的开发,修改和调优生命周期
ii. 开发相关代码有自己要对付的挑战,和非并发代码不同
iii. 即便没有周边应用程序增加的负担,写的不好的并发代码可能的出错方式数量也足够挑战性
iv. 建议:分离并发代码和其他代码
b) 推论:限制数据作用域
i. 谨记数据封装;严格限制对可能被共享的数据的访问
c) 推论:使用数据复本
i. 从一开始就避免共享数据,从多线程收集所有副本的结果,并在单个线程中合并这些结果
d) 推论:线程应尽可能地独立
i. 尝试将数据分解到可被独立线程(可能在不同处理器上)操作的独立子集
4. 了解java库
a) 线程安全群集
i. Java.util.concurrent, java.util.concurrent.atomic java.util.concurrent.locks
5. 了解执行模型
a)
b) 生产者-消费者模型
c) 读者-作者模型 (吞吐量和正常操作)
d) 宴席哲学家
6. 警惕同步方法之间的依赖
7. 保持同步区域微小
8. 很难编写正确的关闭代码
a) 考虑正常关闭问题
9. 测试线程代码
a) 不要将系统错误归咎于偶发事件
b) 不要追踪非线程缺陷和线程缺陷。确保代码可以在线程之外工作
c) 编写可插拔的线程代码,这样就能在不同的配置环境运行
附录:
A1:客户端/服务器段例子
对于并发编程,因其复杂性,应该将全部代码,根据SRP,放到少数类中。
A2:可能执行的路径
理解线程之间如何相互干涉,并不一定要精通字节码文件,只需要知道多个线程之间互相干涉的可能性。
有必要理解内存模型,明白什么是安全的,什么是不安全的。
必须知道什么地方有共享对象/值;哪些代码会导致并发读/写的问题;如何防止这种并发问题发生。
A3: 了解类库
3.2:非锁定的解决方案:
原子类。
CAS(compare and swap )机制,类似于数据库中的乐观锁定,而其同步版本类似于保守锁定。
3.3非线程安全类:
数据库连接、java.util容器、servlet
A4:方法之间的依赖可能破坏并发代码
多线程调用的时候,hasnext()的地方可能击穿
解决的方法:
容忍错误: 相当于电脑坏了重启电脑,并不能解决问题
基于客户端代码的锁定: 不可靠
基于服务端的修改:
可以使用下述方法不改变客户端接口:
A5:提升吞吐量