最近一直在看《java并发编程实践》。
很大收获谈不上,至少见识到了并发世界的广博。
以前对java并发的概念是少之又少,感觉只需要用好synchronized关键字就好。
如今回想,实乃现实版的井中之蛙,狭隘至极。
书大概看完了,梳理下看到的一些点。
关键字
线程安全: 存在线程共享可变对象,则需要考虑线程安全。线程安全的定义:在多线程环境中,能永远保证程序的正确性。
原子性: 在这指的是基于线程的原子性。原子性操作:只能由一个线程单独访问的操作。即在执行该操作时,要么执行完,要么不执行,不再会执行的过程中插入其他线程。
竞态条件: 当程序的正确性取决于线程的执行顺序时。就可能会产生竞态条件。(最常见的竞态条件类型是“先检查后执行”操作)
复合操作: 与原子性操作相反。
内置锁: 每个对象都会有一个内置锁,可以使用synchronized关键字获取该锁,内置锁具有线程互斥性。 (重入: 持有锁的线程具有再次进入锁区域的权限。)
活跃性与性能:
锁粒度与性能: 锁粒度越小,则性能相对会越好。
不变性条件: 契约式编程中的概念。前置条件,不变性条件,后置条件。类在满足不变性条件时才能保持正确性。(分析一个类是否线程安全的重要条件,即在多线程环境中保证不变性条件不被破坏)
可见性: 当一个线程修改某共享对象状态后,其他线程是够能够看到发生的变化。(volatile可保证可见性,却不能保证原子性。synchronized都可保证)
发布与逸出: 发布,是对象能够在当前作用域之外的代码中使用。 当某个不应该发布的对象被发布时,这种情况被称为逸出。(发布的三种形式:作为非私有域发布,作为非私有方法返回,作为参数发布)
线程封闭:一种避免使用同步的方式就是不共享数据。如果仅在单线程内访问数据,就不需要同步。这种技术叫线程封闭。它是实现线程安全最简单的方式之一。(Ad-hoc封闭,栈封闭,ThreadLocal类)
安全发布:
同步策略:定义了如何在不违背对象不变条件或后验条件的情况下对其状态的访问操作进行协同。同步策略规定了如何将不可变性,线程封闭,与加锁机制等结合起来以维护线程的安全性,并且还规定了哪些变量由哪些锁保护。
状态的所有权: 独占控制权。 共享控制权。 所有权分离。
实例封闭:将目标对象封装在类中,并用内置锁控制对该对象的访问。
java监视器模式: 一种编写代码的约定。一直使用某一对象的锁来保护某状态。例如,用唯一的内置锁保护对其所有状态变量的访问。
线程安全委托: 将类的线程安全性委托给某个或多个线程安全的状态变量。(注意多个时,这些变量必须是彼此独立,且不存在相关联的不变性条件。)
同步策略文档化:很重要。
技巧相关
如果多个线程访问同一个可变的状态变量没有使用适当的同步,那么程序就会发生错误,有三种方式可以修复这个问题:
1、不在线程之间共享该状态变量
2、将状态变量修改为不可变的变量。
3、在访问状态变量时使用同步
在线程安全类中封装了必要的同步机制,因此客户端无需进一步采取同步措施。
当且仅当满足一下所有条件时,才应该使用volatile变量:
1、对变量的写入操作部依赖变量的当前值,或者你能确保只有单个线程更新变量的值。
2、该变量不会与其他状态变量一起纳入不变性条件中。
3、在访问变量时不需要加锁。
如何安全发布,
1、在静态初始化函数中初始化一个对象引用
2、将对象的引用保存到volatile类型的域,或者AtomicRefreance中。
3、将对象的引用保存在某个正确构造对象的final类型域中。
4、将对象的引用保存到一个由锁保护的域中。(将对象的引用放入同步容器中也是安全发布。)
对象的发布需求取决于它的可变性:
1、不可变对象可通过任意机制发布。
2、事实不可变对象必须通过安全方式发布。
3、可变对象必须通过安全方式发布,并且必须是线程安全的或者由某个锁保护起来。
并发程序中使用和共享对象时的使用策略:
线程封闭、只读共享、线程安全共享、保护对象。
发布底层状态变量: 如果一个状态变量是线程安全的,并且没有任何不变性条件来约束他们时,在变量的操作不存在任何不允许的状态转换,那么就可以安全地发布这个变量。
扩展现有的线程安全类: 继承, 组合。(遵循其同步策略)
设计相关
不可变对象一定是线程安全的。
封装有利于线程安全管理。(良好的面向对象技术,不可修改性,明晰的不变性规则)
从一开始就设计线程安全的类,比以后再将这个类修改为线程安全的类容易的多。
首先使代码运行,然后再提高代码的速度。
常见的加锁约定:将所有的可变状态都封装在对象内部,并通过对象的内置锁对所有访问可变状态的代码路径进行同步,使得在该对象上不会发生并发访问。
对于每个包含多个变量的不变性条件,其中涉及的所有变量都需要由同一个锁来保护。
封装能够使得对程序的正确性进行分析变得可能,并使得设计约束条件难以被破坏。
设计线程安全类流程:
1、找出构成对象状态的所有变量。
2、找出约束状态变量的不变性条件。
3、建立对象状态的并发访问管理策略。