线程安全基础
最低安全性
当线程在没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个值是由之前某个线程设置的值,而不是一个随机值。这种安全性保证称为最低安全性。
最低安全性适用于绝大多数变量,但是存在一个例外,非 volatile 类型的64位数值变量(double 和 lang)。Java 内存模型要求,变量的读取操作和写入操作必须是原子操作,但对于非 volatile 类型的 long 和 double 变量,JVM 允许将64位的读操作或写操作分解为两个32位的操作。
Volatile 变量
一种稍弱的同步机制,用来确保将变量的更新操作通知到其他线程。当声明为 volatile 类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile 变量不会被缓存到寄存器或者其他处理器不可见的地方,因此在读取 volatile 类型的变量是总会返回最新写入的值。
加锁机制即可以保证可见性又可以保证原子性,而 volatile 变量只能确保可见性。
- volatile 使用条件(当且仅当)
- 对变量的写入操作不依赖变量的当前值,或者确保只有单个线程更新变量值。
- 该变量不会与其他状态变量一起纳入不变性条件中。
- 在访问变量时不需要加锁。
线程封闭
避免使用同步,在单线程内访问数据。可靠的线程封闭技术包括
栈封闭
和ThreadLocal
Ad-hoc 线程封闭
维护线程封闭性的职责完全由程序实现来承担。该方式是非常脆弱的。
可以通过在 volatile 变量上实现一种特殊的线程封闭。
确保只有单个线程对共享的 volatile 变量执行写入操作,
那么就可以安全的就可以安全的在这些共享的 volatile 变量上执行·读取·-·修改·-·写入·的操作。
在这种情况下,相当于将修改操作封闭在单个线程中以防止发生竟态条件,并且 volatile 变量还确保了其可见性。
栈封闭
线程封闭的一种特例,栈封闭中,只能通过局部变量才能访问对象。
局部变量的固有属性之一就是封闭在执行线程中。
它们位于执行线程的栈中,其他线程无法访问这个栈。
ThreadLocal 类
ThreadLocal 提供了 get 和 set 等访问接口和方法。
这些方法为每个使用该变量的线程都存有一份独立的副本,
因此 get 总是返回由当前执行线程在调用 set 时设置的最新值。
不变性
不可变对象
某个对象在创建后其状态不能被修改,那么这个对象就称为不可变对象。
不可变对象很简单,它们只有一种状态,并且该状态由构造函数控制。
Final 域
final 用于构建不可变性对象。final 类型的域是不可修改的(但如果 final 域所引用的对象是可变的,那么这些被引用的对象是可以修改的)
深入理解 final 关键字
final 域能确保初始化过程的安全性,从而可以不受限制的访问不可变对象,并在共享这些对象时无需同步。
安全发布对象
要安全发布一个对象,对象的引用和状态必须同时对其他线程可见。
一个正确构造的对象可以通过以下方式来安全发布。
* 在静态初始化函数中初始化一个对象引用
* 将对象的引用保存到 volatile 类型的域或AtomicReferance中
* 将对象的引用保存到某个正确构造对象的 final 域中
* 将对象的引用保存到由锁保护的域中
网友评论