1.可见性
为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。
如果缺乏同步机制,其他线程可能读取不到一个线程对变量的写操作,更可能由于存在重排序的现象导致其余线程产生一些“奇怪”的现象。
重排序导致:在缺乏足够的同步机制的程序中,要想对内存操作的执行顺序进行判断,几乎无法得到正确的结果
关于重排序可见:
http://www.infoq.com/cn/articles/java-memory-model-2
1.1失效数据
1 public class MutableInteger{ 2 private int value; 3 4 public int get() {return value;} 5 public void set(){this,.value=value;} 6 }
一个线程set,而另外一个执行get方法线程可能得不到value的新值,需要把get和set方法改成同步方法,如下:
1 public class MutableInteger{ 2 private int value; 3 4 public synchronized int get() {return value;} 5 public synchronized void set(){this,.value=value;} 6 }
如果仅对set方法进行同步,get方法仍然会看到失效值
1.2非原子的64位操作
失效值只是之前某个线程设定的值,而不是一个随机值,这种安全性保证也成为最低安全性,最低安全性适用于绝大多数变量,除了非volatile类型的64位数值变量(double和long)。这类数值变量将读写操作分解为两个32位的操作,即使不考虑失效数据问题,页,在多个线程中使用共享可变long和double的变量也是不安全的,除非使用关键字volatile关键字或锁保护起来。
1.3加锁与可见性
在访问某个共享可变变量时要求所有线程在同一个锁上同步,就是为了确保某个线程写入该变量的值对于其他线程是可见的,否则,就有可能读到一个失效值。
加锁的含义不仅仅局限于互斥行为,还包括内存的可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或写操作的线程都必须在同一个锁上同步。
1.4Volatile变量
volatile变量只能确保变量的可见性并不保证原子性。因此通常用于某个操作完成,发生中断或者状态的标志。
使用volatile变量的条件:
(1)对变量的写入操作不依赖变量的当前值,或者只有单个线程更新变量的值
(2)该变量不会与其他状态变量一起纳入不变性的条件中
(3)在访问该变量时不需要加锁
2.发布与逸出
“发布”是指使对象能够在当前作用域之外的的代码中使用。
“逸出”是指某个不应该发布的对象被发布了。
3.线程封闭
3.1Ad-hoc线程封闭
维护线程封闭性的职责完全有程序实现来承担
3.2栈封闭
3.3ThreadLocal类
4.不变性
不可变对象一定是线程安全的。
当满足一下条件时,对象才是不可变的:
(1)对象创建以后其状态就不能改变
(2)对象的所有域都是final类型
(3)对象是正确创建的(在对象的创建期间,this引用没有逸出)
4.1Final域