3.1 可见性
volatile关键字,将变量放在内存中,是内存的可见性,用来确保变量的更新操作通知到其他线程。这样不会出现多个线程访问变量时,变量被修改而无法感知的情况,volatile无法保证原子性。
public class NoVisblility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread{
@Override
public void run() {
while (!ready) {
Thread.yield();
System.out.println(number);
}
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
博主将jdk降到了1.5还是没有出现ready没有被感知的情况,(有了解的可以请告诉我~,谢谢)。
3.2 发布与逸出
发布Pulish
指将一个指向该对象的引用保存到其他代码可以访问的地方,或将引用传递到其他类的方法中。
逸出Escape
在对象构造完成之前就发布该对象,就会破坏线程安全性。这种情况称为逸出
public class ThisEscape {
//隐式使this逸出 尽量不要这么做
public ThisEscape(EventSource source) {
source.registerListener(new EventListener() {
@Override
public void onEvent(Event e) {
doSomething(e);
}
});
}
}
多线程情况下对象逸出造成输出不一致,出现这种情况是因为在构造函数执行完之前都不算完成。
当且仅当对象的构造函数返回时,对象才处于可预测的和一致的状态。因此,当对象的构造函数发布对象时,只是发布了一个尚未构造完成的对象。
只有当构造函数返回时,this引用才应该从线程中逸出。构造函数可以将this引用保存到某个地方,只要其他线程不会在构造函数完成之前使用它。
为什么这样做:
1.原本的调用在多线程的情况下防止误用,告知对象的是在构造函数完成时被发布的。
2.使用safe方式写,可以完全的隐藏对象,安全的对对象进行构造,防止任何误操作。
public class SafeListener {
private EventListener listener;
private SafeListener() {
listener = new EventListener() {
@Override
public void onEvent(Event e) {
doSomething(e);
}
};
}
/**
* 只有当构造函数返回时,this引用才应该从线程中逸出。构造函数可以将this引用保存到某个地方,
* 只要其他线程不会在构造函数完成之前使用它。
*/
public static SafeListener newInsatance(EventSource source){
SafeListener safeListener = new SafeListener();
source.registerListener(safeListener.listener);
return safeListener;
}
}
3.3 线程封闭
1.Ad-hoc 线程封闭,指维护线程封闭的职责完全由程序实现来承担。Ad-hoc线程封闭技术脆弱,在程序中尽量不要使用。
2.栈封闭,是局部变量才能访问的对象,局部变量的固有属性就是封闭在执行线程中。它们位于执行线程的栈中,其他线程无法访问这个栈。
3.ThreadLocal类使线程中的某个值与保存的对象关联起来。每个使用该变量的线程都有一个独立的副本。ThreadLocal常用于防止对象可变的danbli单实例变量或全局变量进行共享。
3.4 不变性
不变性条件
1.对象创建后的状态不可修改
2.对象的域是final
3.对象创建期间,this引用没有逸出
3.5 安全发布