可见性
并发程序对变量的写入造成变量状态的改变后,其它线程能够即时的读到完整的、最新的值。
volatile
加锁机制既能确保可见性也能确保原子性,volatile只能确保可见性;
volatile变量的使用条件
- 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值;
- 该变量不会与其它状态变量一起纳入不变性条件中;
- 在访问变量时不需要加锁;
发布与逸出章节中,逸出的几种可能
- 如果从非私有方法中返回一个引用,就会发布返回的对象
- 当发布一个对象时,在该对象的非私有域中引用的所有对象同样会被发布,此发布关系存在传递关系
- 最后一种发布对象或其内部状态的机制就是发布一个内容的类实例;
如果this引用在构造过程中逸出,那么这种对象就被认为是不正确构造
几种对象构造过程中的逸出情况以及处理方法
- 在构造函数中启动一个线程,this会逸出到新的线程中,如果新线程直接启动,就可能访问到未就绪的对象。这个时候,可以考虑在构造函数之后,再通过start或者initialize方法来延迟启动线程;
- 如果想在构造函数中注册一个事件监听器或者启动线程,那么可以使用一个私有的构造函数和一个公共的工厂方法,从而避免不正确的构造过程。
线程封闭的几种可靠类型
- 栈封闭 局部方法中,只要确保变量不会逸出,就可以实现线程封闭,是线程安全的。
- ThreadLocal 每一个线程都会来存有一个变量的副本,这个副本是通过实现ThreadLocal的initValue方法来初使化的,但要注意,如果我们使用set方法设置了变量,就需要特别注意,不能让这个对象在多个线程之间被共享,否则仍然会突破线程封闭的限制。
不可变对象是线程安全的,那如何定义不可变对象呢?
- 对象创建以后其状态就不能修改
- 对象的所有域都是final域
- 对象是正确创建的(对象的创建过程中,this引用没有逸出)
缩小域范围的劝告
正如“除非需要更高的可见性,否则应将所有的域都声明为私有域”,是一个良好的编程习惯,“除非需要某个域是可变的,否则就将所有的域都声明为final域”,也是一个良好的编程习惯。
安全发布的方式
- 静态初始化方式法中初始化一个对象引用
- 将对象的引用保存在volatile类型的域或者atomicReferance对象中
- 将对象的引用保存到某个正确构造对象的final域中
- 将对象的引用保存到一个由锁保护的域中
在没有额外的同步的情况下,任何线程都可以安全地使用被安全发布的事实不可变对象
对象的发布需求取决于它的可见性
- 不可变对象可以以任何方式发布
- 事实不可变对象必须以安全方式发布
- 可变对象必须以安全方式发布,并且必须是线程安全或者某个由锁保护起来
并发程序中使用与共享对象时的一些实用策略
- 线程封闭。只能由一个线程拥有,因此不需要考虑同步的问题。
- 只读共享。包括不可变对象以及事实不可变对象。
- 线程安全共享。在类内部实现同步,因此多个线程可以通过对象的公有接口进行访问而不需要进一步的同步
- 保护对象。只能通过持有某个锁来访问对象,包括锁某个区域以及封装在一些线程安全的类里边等;