synchronized不仅可以保证原子性,确定临界区,还可以保证可见性
1.可见性
1.1失效数据
- 如果对象无法保证可见性,那么在对象数据状态发生变化之后,其他线程无法获取对象属性的最新值,此时就会得到一个
失效数据
- 一个线程安全的类,不但set方法要使用synchronized,get方法同样要使用synchronized关键字,因为get方法要使用synchronized来保证可见性。如下面代码中的MutableInteger就不是一个线程安全类:
public class MutableInteger {
private int value;
public int getValue() {
return value;
}
public synchronized void setValue(int value) {
this.value = value;
}
}
get方法使用synchronized后,即可保证这个类成为一个线程安全类:
public class MutableInteger {
private int value;
public synchronized int getValue() {
return value;
}
public synchronized void setValue(int value) {
this.value = value;
}
}
1.2非原子的64位操作
- 在线程没有同步的情况读取变量,可能会读取到一个失效值,但这至少是之前某个线程设置的值,而不是一个随机值,这种安全性保证称为
最低安全性
- 但是对于像
Long````Double
这种64位对象,在JVM进行读取、写入时,是拆分位两条指令进行操作的,因此对于它们的操作可能会产生不可预知的结果,对于这类对象,保证线程安全性必须使用volatile关键字或使用锁进行保护
加锁的含义不仅包含
互斥行为
,还包含内存可见性
。为了保证所有线程都能看到共享变量的最新值,所有执行读取和写入操作的线程必须在统一把锁上进行同步
1.3volatile
- 当一个变量被声明为volatile时,编译器与运行时都会注意到这个变量是共享的,不会将对于此变量的操作与其他内存操作做
重排序
。volatile不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile变量时,总是可以读取到最新值 - 加锁机制既可保证原子性,又可保证内存可见性,但是volatile只能保证内存可见性
满足以下条件时,才可使用volatile:
对变量的写入操作不依赖与变量的当前值,或者确认只有一个线程会对当前线程执行写入操作
该变量不会与其他状态变量一起纳入不变性条件中
在访问变量时不需要加锁
2.发布与逸出
发布是指使对象可以被当前作用域之外的代码所访问
当某个不该发布的对象被发布时,就称为逸出,例如在对象尚未实例化完成时,就将对象发布,此时就会造成逸出
3.线程封闭
如果数据只能在单线程内访问即只在当前线程中访问,那么数据就不需要同步,这种技术称为线程封闭
3.1Ad-hoc线程封闭
Ad-hoc线程封闭是指维护线程封闭性的职责完全由程序来实现
例如volatile变量存在一种特殊的线程封闭,只要能确保volatile变量只能被一个线程所修改,那么就可以保证变量的线程安全性。
3.2栈封闭
栈封闭是一种特殊的线程封闭,它全部使用局部变量将对象封装在线程中,保证线程安全性
3.4ThreadLocal类
ThreadLocal类是JDK提供的对于每个线程独有的存储区。但是不要滥用ThreadLocal,例如使用ThreadLocal来进行一些不必要的参数传递,既没有提高程序性能,反而会增加代码理解的复杂度
4不变性
如果某个对象被创建之后其状态就不可能被修改,那么这个对象就被称为不可变对象,不可变对象一定是线程安全的
满足以下条件时,对象才是不可变的:
- 对象创建后其状态就不能修改
- 对象的所有域都是final类型的
- 对象是正确创建的(对象创建期间this引用没有逸出)
Final域能确保初始化过程的安全性,从而可以不受限制的访问不可变对象,并在共享这些对象时无需同步
5 安全发布
任何线程都可以在不需要额外同步的情况下安全访问不可变对象,即使在发布这些对象时没有使用同步
安全发布对象的方式:
- 在静态初始化函数中初始化一个对象引用
- 将对象引用保存到volatile类型域或者AtomicReference对象中
- 将对象引用保存到某个正确构造的final类型域中
- 将对象引用保存到一个由锁保护的域中
对象发布的原则:
- 不可变对象可以通过任何机制来进行发布
- 事实不可变对象必须通过安全方式来进行发布
- 可变对象必须通过安全方式来进行发布,并且必须是线程安全的或者由某个锁来进行保护
注 : 事实不可变对象 指对象是可变的,但是其状态在对象发布后在实际运行过程中不会发生变化
网友评论