1、可见性
只要数据需要被跨线程共享,就进行恰当的同步。
一个线程在没有同步的情况下读取变量,它可能会得到一个过期值。但它至少可以看到某个线程在那里设置的一个真实数值,而不是一个凭空而来的值。这样的安全保证被称为是最低限的安全性。
锁不仅仅是关于同步与互斥的,也是关于内存可见的。为了保证所有线程都能够看到共享的、可变变量的最新值,读取和写入线程必须使用公共的锁进行同步。
只有当volatile变量能够简化实现和同步策略的验证时,才使用它们。当验证正确性必须推断可见性问题时,应避免使用volatile变量。正确使用volatile变量的方式包括:用于确保它们锁引用的对象状态的可见性,或者用于标识重要的生命周期时间(比如初始化或关闭)的发生。
volatile boolean asleep; … while(!asleep) countSomeSheep();
加锁可以保证可见性与原子性;volatile变量只能保证可见性。
只有满足下面所有标准后,你才能使用volatile变量:
1)写入变量时并不依赖变量的当前值;或者能够确保只有单一的线程修改变量的值;
2)变量不需要与其他的状态变量共同参与不变约束;
3)而且,访问变量时,没有其他的原因需要加锁。
2、发布和逸出
发布一个对象的意思是使它能够被当前范围之外的代码锁使用。一个对象在尚未准备好时就将它发布,这种情况称作逸出。
3、线程封闭
当对象封闭在一个线程中时,这种做法会自动成为线程安全的,即使被封闭的对象本身并不是。
一种维护线程限制的更加规范的方式是使用ThreadLocal,它允许你将每个线程与持有数值的对象关联在一起。ThreadLocal提供了get与set访问器,为每个使用它的线程维护一份单独的拷贝。所以get总是返回由当前执行线程通过set设置的最新值。
4、不可变性
不可变对象永远是线程安全的。
只有满足如下状态,一个对象才是不可变的:
1)它的状态不能在创建后再被修改;
2)所有域都是final类型;
3)它被正确创建(创建期间没有发成this引用的逸出)。
5、安全发布
为了安全的发布对象,对象的引用以及对象的状态必须同时对其他线程可见。一个正确创建的对象可以通过下列条件安全地发布:
1)通过静态初始化器初始化对象的引用。
2)将他的引用存储到volatile域或AtiomicReference。
3)将它的引用存储到正确创建的对象的final域中。
4)或者将它的引用存储到由锁正确保护的域中。