单例模式是咱们比较经常使用的设计模式,玩好单例模式也会涉及到不少java基础知识。
单例做为全局性实例,在多线程状况下全局共享的变量会变得很是危险。java
双重检测是比较经常使用的一种实现方式:设计模式
public class Singleton { public static final volatile Singleton singleton = null; private Singleton(){} public static Singleton getInstance(){ if(singleton == null){ synchronize (Singleton.class){ if( singleton == null ) { singleton = new Singleton(); } } return singleton; } }
若是不用volatile修饰,多线程执行到 singleton == null 时,多个实例会被建立出来,就可能形成内存泄露问题。多线程
固然你能够说能够用互斥同步的方式进行,可是咱们作了同步,多线程的操做就变成了串型了,效率会很低,由于建立对象其实只须要一次,可是后面的读取都须要同步了。jvm
还有一个缘由,在jvm编译器可能会对指令进行重拍和优化,就是判断singleton == null的判断顺序可能没法保证。
因而咱们将变量用volatile修饰,这个变量就不会在多线程中存在副本,都必须从主内存读取,同时避免了指令重拍。优化
当两个线程执行完第一个 singleton == null 后等待锁, 其中一个线程得到锁并进入synchronize后,实例化了,而后退出释放锁,另一个线程得到锁,进入又想实例化,会判断是否进行实例化了,若是存在,就不进行实例化了。线程
一个延迟实例化的内部类的单例模式,一个内部类的容器,调用getInstance时,JVM加载这个类设计
public final class Singleton { private static class SingletonHolder { static final Singleton INSTANCE = new Singleton(); } private Singleton() {} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
因为SingleHolder是私有的,除了getInstance()以外没有方法能够访问它,只有在getInstance()被调用时才会真正建立,code
首先,其余类在引用这个Singleton的类时,只是新建了一个引用,并无开辟一个的堆空间存放(对象所在的内存空间)。
接着,当使用Singleton.getInstance()方法后,Java虚拟机(JVM)会加载SingletonHolder.class(JLS规定每一个class对象只能被初始化一次),并实例化一个Singleton对象。对象
缺点:内存
须要在Java的另一个内存空间(Java PermGen 永久代内存,这块内存是虚拟机加载class文件存放的位置)占用一个大块的空间。