单例模式
1、概述:
确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一的实例。
2、三要点:
(1)某个类只能有一个实例
(2)必须自行创建这个实例
(3)必须自行向整个系统提供这个实例
3、实现:
public class Singleton(){
private static Singleton instance=null;
//构造函数私有化,使外界要new该类时,只能通过方法去创建实例。
private Singleton(){
}
//静态公有工厂方法,检验实例的存在性并实例化自己,
//然后存储在静态成员变量中。
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
注意:
(1)单例类的构造函数的可见性为private。
(2)提供一个类型为自身的静态私有成员变量。
(3)提供一个公有的静态工厂方法。
4、扩展
(1)饿汉式单例:着急于实例化类,在定义时便实例化单例类。
public class EagerSingleton(){
//当类被加载时,静态变量instance会被初始化,此时类的私有
//构造函数会被调用,单例类的唯一实例将被创建。
private static final EagerSingleton instance=new EagerSingleton();
//私有构造
private EagerSingleton(){
}
public static EagerSingleton getInstance(){
return instance;
}
}
(2)懒汉式单例:对于实例化类呈懒惰态度,即实例化的时机在第一被引用时触发,在类进行加载时,不会将自己实例化(延迟加载技术)。
public class LazySingleton(){
private volatile static LazySingleton instance=null;
private LazySingleton(){
}
//synchronized关键字,确保任意时刻只有一个线程执行
synchronized public static LazySingleton getInstance(){
if(instance==null){
instance=new LazySingleton;
}
return instance;
}
}
发现synchronized锁住了整个方法,导致多线程高并发时,系统性能降低。解决办法:仅仅锁住instance=new LazySingleton。
public static LazySingleton getInstance(){
if(instance==null){
synchronized(LazySingleton.class){
instance=new LazySingleton;
}
}
return instance;
}
虽然解决了性能问题,但是这样会存在对象不唯一的情况。
原因:当线程A和线程B同时调用getInstance方法时,两者都执行到instance==null,均通过判断,此时A进入锁定的代码区,B进行排队等待。当线程A执行完毕时,线程B并不知道此时实例已经创建,将继续创建新的实例。最后导致产生的实例不唯一。
解决办法:在被锁定的的代码块中再次进行instance==null的判断(双重检查锁定)
public static LazySingleton getInstance(){
//第一重判断
if(instance==null){
synchronized(LazySingleton.class){
//第二重判断
if(instance==null){
instance=new LazySingleton;
}
}
}
return instance;
}
注意:使用双重检查锁定来实现懒汉式单例,需要在静态成员变量instance之前增加修饰符volatile,但volatile会屏蔽JVM所做的一些代码优化,可能会导致系统的运行效率降低,因此使用双重检查锁定来实现单例模式也不是一种完美的实现方式。
(3)静态内部类实现单例模式(推荐使用):懒加载,且保证线程安全。
静态内部类特点:当类进行装载的时候,静态内部类不会被装载。当使用到静态内部类的静态变量时,会导致静态内部类被装载,且只装载一次,JVM的加载机制保证了线程安全。
public class InnerSingleton(){
private InnerSingleton(){}
private static class InnerSingletonInstance(){
private static final InnerSingleton INSTANCE=new Singleton();
}
private static getInstance(){
return InnerSingletonInstance.INSTANCE;
}
}
(4)枚举实现单例模式(最完美的单例实现方法,不过用得挺少):
以上的方法都声明了一个私有构造,但仍然能够被反射所破坏进行对象的创建。
Singleton instance1=Singleton.INSTANCE;
Singleton instance2=Singleton.INSTANCE;
//instance1与instance2两者相同
enum Singleton{
INSTANCE;//Singleton的一个属性
}
5、使用场景:
(1)需要频繁地进行创建和销毁的对象。
(2)创建对象时耗过多或耗费资源过多,但又经常用到的对象。
(3)工具类对象,频繁访问数据库或文件的对象。