共享对象
可见性
确保跨线程写入的内存可见性,必须使用同步机制。
案例:
public class NoVisibliity{
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread{
public void run(){
while(!ready){
Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args){
new ReaderThread().start();
number=42;
ready=true;
}
}
}
主程序运行可能会出现0的情况,程序运行存在“重排序”的问题。
过期数据
导致意外的异常、脏数据、错误的计算和无限的循环。
非原子性的64位操作
double long数值变量最好声明为volatile类型或用锁保护起来,JVM将64位读写划分为两个32位操作,就有可能出现问题。
锁和可见性
- 内置锁保证了可见性。
- 读取和写入线程必须使用公共的锁进行同步。
Volatile变量
- 一种同步的弱形式
- 相比于synchronized而言,是轻量级的同步机制。
- 写入volatitle变量就类似于退出同步块,读取就是进入同步块。
- 加锁可以保证原子性和可见性,volatitle只能保证可见性。
- 一般用在标识、中断、状态的标记
案例:
volatitle boolean asleep;
...
while(!asleep){
...
//
}
- 使用条件:
1、写入变量并不依赖变量的当前值。
2、变量不需要与其他状态共同参与不变约束。
3、访问变量时,没有其他原因需要加锁。
发布和逸出
1、发布
- 方法持有一个对象的引用。
- 发布的方法:将对象的引用存储到公共静态域中。
public static Set<Secret> knowSecrets;
public void initialize(){
knowSecrets=new HashSet<Secret>();
}
- 隐式允许this逸出
内部类暴露
public class ThisEscape{
public ThisEscape(EvenetSource source){
new EventListener(){
public void onEvent(Event e){
doSomething(e);
}
}
}
}
2、逸出
- 如果发布对象时,他还没有完成构造。
3、安全发布
- 构造函数
- 使用工厂方法防止this逸出
如果想在构造函数中注册监听器或启动线程,可以使用一个私有构造函数和一个公共的工厂方法。
public class Listener{
private final EvenetListener listener;
private Listener(){
listener=new EventListener(){
public void onEvent(Event e){
doSomething(e);
}
};
}
public static Listener newInstance(EvenetSource source){
Listener safe=new Lisener();
source.registerListener(safe):
return safe;
}
线程封闭
- 利用ThreadLocal实现