编写正确的并发程序,关键的问题在于:在访问共享的可变状态时需要进行正确的管理
关键字synchronized
作用:
- 实现原子性(Atomic)以及确定临界区(Critical Section)
- 内存可见性(Memory Visibility)
注:我们不仅希望防止某个线程正在使用对象状态而另一个线程在同时修改该状态,而且希望确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。
第 3 章 对象的共享
3.1 可见性
3.1.2 非原子的 64 位操作
最低安全性(out-of-thin-air-safety)
- 概念:
当线程在没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个值是由之前某个线程设置的值,而不是一个随机值。 - 最低安全性适用于绝大多数变量,但是存在一个例外:
非 volatile 类型的 64 位数值变量(double 和 long)
3.1.3 加锁与可见性
- 为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。
3.1.4 Volatile 变量
1、当把变量声明为 volatile 类型后,编译器与运行时都会知道这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。
2、Volatile 变量不会缓存在寄存器或者对其它处理器看不见的地方,因此在读取 volatile 类型的变量时总会返回最新的值。
3、从内存可见性的角度来看,写入 volatile 变量相当于退出同步代码块,读取 volatile 变量相当于进入同步代码块。
4、仅当 volatile 变量能简化代码的实现以及对同步策略的验证时,才应该使用它们。
5、Volatile 变量的正确使用方式包括:确保它们自身状态的可见性,确保它们所引用的对象的状态的可见性,以及标识一些重要的程序生命周期事件的发生(例如,初始化或关闭)。
6、Volatile 变量通常用作某个操作完成、发生中断或者状态的标志,典型用法:检查某个状态标记以判断是否退出循环。
7、Volatile 语义不足以确保递增操作(count++)的原子性,而原子变量则能提供“读-改-写”的原子操作。
8、加锁机制既可以确保内存可见性
又可以确保原子性
,而 volatile 变量只能确保内存可见性
。
9、当且仅当满足以下条件时,才应该使用 volatile 变量:
- 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值;
- 该变量不会与其他状态变量一起纳入不变性条件中;
- 在访问变量时不需要加锁。
3.2 发布与逸出
发布(Publish)
一个对象的意思是指:使对象能够在当前作用域之外的代码中使用。
逸出(Escape)
是指:当某个不应该发布的对象却被发布了。
发布对象一般有如下 3 种方式:
- 将一个指向该对象的引用保存到其他代码可以访问的地方,例如将一个对象的引用保存到一个公有的静态变量
中,以便任何类和线程都能看到该对象;
- 在某个非私有的方法
中返回该对象的引用;
- 将该对象的引用传递到其他类的方法中。