对象发布:就是提供一个对象的引用给做用域以外的代码。好比return一个对象,或者做为参数传递到其余类的方法中。java
对象逸出:一种错误的发布,当一个对象尚未构造结束就已经提供给了外部代码一个对象引用即发布了该对象,此时叫作对象逸出,对象的逸出会破坏线程的安全性。数组
咱们最须要关注的就是对象逸出的问题,在不应发布该对象的地方就不要发布该对象,例如如下代码:安全
class UnsafeStates{ private String[] states = new String[]{"AK", "AL"}; //states变量做用域是private而咱们在getStates方法中却把它发布了, //这样就称为数组states逸出了它所在的做用域。 public String[] getStates(){ return states; } public static void main(String[] args) { UnsafeStates unsafeStates = new UnsafeStates(); //此处咱们经过getStates方法对private修饰的states作出更改。(对象逸出了) unsafePublish.getStates()[0] = "AA"; } }
咱们再来看看更加隐秘的this逸出,那么什么是this逸出?观察如下代码:多线程
public class ThisEscape{ private int value; public ThisEscape(EventSource source){ source.registerListener{ //当事件监听类注册完毕后, //实际上咱们已经将EventListener匿名内部类发布出去了 //而此时咱们的一些初始化工做尚未完成 //也就是一个类尚未构造完成已经将对象发布出去了 new EventListener(){ public void onEvent(Event e){ doSomething(e); } } } //一些初始化工做 value = 7; } public void doSomething(Event e){ System.out.println(value);//this对象逸出,有可能在构造方法初始化的时候一些初始工做未完成而提早发布了对象从而致使对象逸出的问题 } }
线程不安全的,在多线程环境下若是同时有多个线程经过getInstance建立SingletonExample1实例,有可能致使SingletonExample1实例的屡次建立,影响程序逻辑。并发
/** * 懒汉模式 * 单例实例在第一次使用时进行建立 */ public class SingletonExample1 { // 私有构造函数 private SingletonExample1() { } // 单例对象 private static SingletonExample1 instance = null; // 静态的工厂方法 public static SingletonExample1 getInstance() { if (instance == null) { instance = new SingletonExample1(); } return instance; } }
线程安全的,在SingletonExample2首次加载时建立对象。函数
/** * 饿汉模式 * 单例实例在类装载时进行建立 */ public class SingletonExample2 { // 私有构造函数 private SingletonExample2() { } // 单例对象 private static SingletonExample2 instance = new SingletonExample2(); // 静态的工厂方法 public static SingletonExample2 getInstance() { return instance; } }
线程不安全的,双重检测机制通常不会发生线程安全问题,可是不可避免有可能会出现线程安全问题,好比发生了指令重排。高并发
/** * 懒汉模式 -》 双重同步锁单例模式 * 单例实例在第一次使用时进行建立 */ public class SingletonExample4 { // 私有构造函数 private SingletonExample4() { } // 一、memory = allocate() 分配对象的内存空间 // 二、ctorInstance() 初始化对象 // 三、instance = memory 设置instance指向刚分配的内存 // JVM和cpu优化,发生了指令重排。有可能会致使还未初始化完成的对象已经被别的线程所使用。 // 一、memory = allocate() 分配对象的内存空间 // 三、instance = memory 设置instance指向刚分配的内存 // 二、ctorInstance() 初始化对象 // 单例对象 private static SingletonExample4 instance = null; // 静态的工厂方法 public static SingletonExample4 getInstance() { if (instance == null) { // 双重检测机制 // B synchronized (SingletonExample4.class) { // 同步锁 if (instance == null) { instance = new SingletonExample4(); // A - 3 } } } return instance; } }
线程安全的,volatile会禁止指令重排性能
/** * 懒汉模式 -》 双重同步锁单例模式 * 单例实例在第一次使用时进行建立 */ @ThreadSafe public class SingletonExample5 { // 私有构造函数 private SingletonExample5() { } // 一、memory = allocate() 分配对象的内存空间 // 二、ctorInstance() 初始化对象 // 三、instance = memory 设置instance指向刚分配的内存 // 单例对象 volatile + 双重检测机制 -> 禁止指令重排 private volatile static SingletonExample5 instance = null; // 静态的工厂方法 public static SingletonExample5 getInstance() { if (instance == null) { // 双重检测机制 // B synchronized (SingletonExample5.class) { // 同步锁 if (instance == null) { instance = new SingletonExample5(); // A - 3 } } } return instance; } }
线程安全的,但并不推荐,由于每次经过getInstance获取对象时都会加锁,而影响程序在高并发下的性能。优化
// 私有构造函数 private SingletonExample3() { } // 单例对象 private static SingletonExample3 instance = null; // 静态的工厂方法 public static synchronized SingletonExample3 getInstance() { if (instance == null) { instance = new SingletonExample3(); } return instance; }
线程安全而且是推荐的写法。this
/** * 枚举模式:最安全 */ public class SingletonExample7 { // 私有构造函数 private SingletonExample7() { } public static SingletonExample7 getInstance() { return Singleton.INSTANCE.getInstance(); } private enum Singleton { INSTANCE; private SingletonExample7 singleton; // JVM保证这个方法绝对只调用一次 Singleton() { singleton = new SingletonExample7(); } public SingletonExample7 getInstance() { return singleton; } } }
线程安全的,推荐使用的一种建立单线程的方式
/** * 懒汉式,静态内部类的方式 */ public class Singleton { private static class LazyHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton() { } public static final Singleton getInstance() { return LazyHolder.INSTANCE; } }