对并发/并行编程的总结
1. 并发和并行:一个好的并发算法未必是一个好的并行算法
2. 并发编程的2个设计要点:安全和性能,在多CPU环境下还要考虑可扩展性
3. 安全性:
a. 最常见的方法是要求对象在任意时刻都应该满足一致的状态(不变约束)
b. 在某些无阻塞算法中,允许对象暂时处于不一致状态,但是应该在运行中检测并立刻修正它
4. 影响安全的因素:原子性、可见性和指令顺序
5. 满足安全性的基本策略:
a. 通过对象的不可变性保证对象总是满足不变约束:一个不变对象是线程安全的
a. 无状态变量的对象,静态方法
b. 所有状态变量均不可变的对象
c. 可以尝试把原来可变的对象变成不可变对象(使用建立新实例再替换的方法)以减少同步的要求
d. 实现不变对象要特别注意:在构造函数完全前,不能使用对象!危险在于构造函数中对this的泄漏(对象的逸出问题)
b. 同步
a. 锁
b. 原子变量
c. 其他工具类 (信号量,监视器)
e. 防止死锁 (顺序化资源)
d. 限制:限制对对象的并发访问
6. 提高扩展性的策略:
关键在于减少串行代码:
a. 减少锁的使用
a. 是否可以允许过期数据?
a.
a. 减少锁的范围,有可能阻塞的操作应该排除在锁的范围外,将共享变量的内容保存到本地变量,然后再锁的范围外操纵本地变量
b. 分解锁
对互相独立的共享变量,可以使用不同的锁(分拆锁);例子:对队列,可以使用2个锁分别锁定头尾
使用读写锁来代替普通的锁;
可以考虑用多个锁来代替一个大锁(分离锁),JDK1.5总的并发hashmap使用了16个锁来代替一个大锁以提高吞吐率,同样,在计算hashmap.size时,将分别计算各部分的size再汇总,以避免锁定整个对象
c. 当在一个操作中需要同步多个共享变量时,为了减少同步多个变量带来的复杂性,可将多个变量放入一个单独的状态对象,然后使用新建并替换的方式减少甚至消除同步的要求
d. 某些情况下可以用原子变量来代替锁
e. 使用非阻塞算法来代替锁
f. 遍历集合很难避免锁,但是可以考虑使用版本化迭代变量或者缓存的数据来减少锁的需要
g. 考虑使用不变量/只读适配器(p98)
7. 线程协调和依赖
8. 管理线程