首先什么是单例模式?为什么需要单例模式? 答:单例模式顾名思义就是在整个运行时,一个类只有一个实例对象
因为有的类的实例对象创建和销毁对资源来说消耗不大,但是有些类比较庞大和复杂如果频繁的创建和销毁对象,并且这些对象完全是可以复用的,会造成不必要的性能浪费
单例模式有多种写法需要考虑:
是否线程安全 是否是懒加载 能否能被反射破坏(这个不用考虑一般都是会被破坏的) 不会满足以上三点的单例
如下:首先来一个简单的单例
public class Singleton { private Singleton(){} //构造器私有 private static Singleton instance = null; //初始化对象为null public static Singleton getInstance(){ if (instance == null){ instance = new Singleton(); } return instance; } }
上面代码如果其他类要使用singleton对象的话只能通过调用getInstance方法,进入方法后首先判断是否被构造过,如果是就直接使用,如果不是就new一个新的出来再使用。
如果要保证在同一时刻只有一个线程进入把代码改成如下代码:
public class Singleton { private Singleton(){} private static Singleton instance = null; public static synchronized Singleton getInstance(){ if (instance == null){ instance = new Singleton(); } return instance; } }
但是以上写法不是懒加载的。
我们现在看第二次写的代码,为何性能影响大,原因就在于在方法上定义了synchronized关键字,所以每次进入该方法的时候就必须首先去获取锁。
所以我们把代码改成如下:
public class Singleton { private static Singleton singleton; private Singleton(){} public static Singleton getSingleton(){ if (singleton == null){ synchronized (Singleton.class){ if (singleton == null){ singleton = new Singleton(); } } } return singleton; } }
以上代码运用了双检锁,双检锁是一种很常见的同步代码手段。
下面使用volatile就会阻止指令重排问题
public class Singleton { private volatile static Singleton singleton; private Singleton(){} public static Singleton getSingleton(){ if (singleton == null){ synchronized (Singleton.class){ if (singleton == null){ singleton = new Singleton(); } } } return singleton; } }
上面这种写法既满足了懒加载,又满足了线程安全问题,效率也比较高。
那还有没有更简洁的写法,如下:内部类的写法
`public class Singleton { private static class SingletonHolder{ private static final Singleton INSTANCE = new Singleton();
} private Singleton(){} public static final Singleton getInstance(){ return SingletonHolder.INSTANCE; } }`
这里,静态内部类在程序启动的过程中不会加载,只有在第一次被调用的时候才会被加载。这里主要是运用了jdk加载特性实现了懒加载。
但是上面我们说了会被反射破坏,但是可以利用枚举类型来不让反射破坏,但是枚举类型无法实现懒加载模式。
第二种饿汉式如下:
`public class Singleton { //饿汉式如下: private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}`
- 优点 :
1.线程安全
2.在类加载的同时已经创建好一个静态对象,调用时反应速度快 - 缺点 :
资源效率不高,可能getInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName) ,那么这个实例仍然初始化