3.1 可见性

3.1.1 失效数据

3.1.2 非原子的64位操作

3.1.3 加锁与可见性

3.1.4 Volatile变量

当且仅当满足以下所有条件时使用:

  • 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值
  • 该变量不会与其他状态变量一起纳入不变性条件中
  • 在访问变量时不需要加锁

3.2 发布与逸出

发布(Publish):使对象能够在当前作用域之外的代码中使用。当某个不应该发布的对象被发布时,这种情况就被称为逸出(Escape)

3.3 线程封闭(Thread Confinement)

仅在单线程内访问数据

3.3.1 Ad-hoc线程封闭

维护线程封闭性的职责完全由程序实现来承担

3.3.2 栈封闭

只能通过局部变量才能访问对象

3.3.3 ThreadLocal类

使线程中的某个值与保存值的对象关联起来

3.4 不变性

当满足以下条件时,对象才是不可变的:

  • 对象创建以后其状态就不能修改
  • 对象的所有域都是final类型
  • 对象是正确创建的(在对象的创建期间,this引用没有逸出)

3.4.1 Final域

3.5 安全发布

3.5.1 不正确的发布:正确的对象被破坏

不能指望一个尚未被完全创建的对象拥有完整性

3.5.2 不可变对象与初始化安全性

状态不可修改,所有域都是final类型,以及正确的构造过程

3.5.3 安全发布的常用模式

一个正确构造的对象可以通过以下方式来安全地发布:

  • 在静态初始化函数中初始化一个对象引用
  • 将对象的引用保存到volatile类型的域或者AtomicReferance对象中
  • 将对象的引用保存到某个正确构造对象的final类型域中
  • 将对象的引用保存到一个由锁保护的域中

线程安全库中的容器类提供了以下的安全发布保证:

  • 通过将一个键或者值放入Hashtable、synchronizedMap或者ConcurrentMap中,可以安全地将它发布给任何从这些容器中访问它的线程(无论是直接访问还是通过迭代器访问)
  • 通过将某个元素放入Vector、CopyOnWriteArrayList、CopyOnWriteArraySet、synchronizedList或synchronizedSet中,可以将该元素安全地发布到任何从这些容器中访问该元素的线程
  • 通过将某个元素放入BlockingQueue或者ConcurrentLinkedQueue中,可以将该元素安全地发布到任何从这些队列中访问该元素的线程

3.5.4 事实不可变对象(Effectively Immutable Object)

对象从技术上来看是可变的,但其状态在发布后不会再改变

3.5.5 可变对象

对象的发布需求取决于它的可变性:

  • 不可变对象可以通过任意机制来发布
  • 事实不可变对象必须通过安全方式来发布
  • 可变对象必须通过安全方式来发布,并且必须是线程安全的或者由某个锁保护起来

3.5.6 安全地共享对象

在并发程序中使用和共享对象时,可以使用一些实用的策略,包括:

  • 线程封闭。线程封闭的对象只能由一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改
  • 只读共享。在没有额外同步的情况下,共享的只读对象可以由多个线程并发访问,但任何线程都不能修改它。共享的只读对象包括不可变对象和事实不可变对象
  • 线程安全共享。线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有接口来进行访问而不需要进一步的同步
  • 保护对象。被保护的对象只能通过持有特定的锁来访问。保护对象包括封装在其他线程安全对象中的对象,以及已发布的并且由某个特定锁保护的对象