一.单例模式介绍
单例模式(Singleton),也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
二.适用场景
单例模式在多线程的环境中需要小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么同时检测到没有唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被创建了出来,从而违反了单例模式中实例唯一的原则。解决这个问题的办法是为知识类是否已经实例化的变量提供一个互斥锁,虽然这样会降低效率。
优点:
1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是一个相同的实例。这样就防止其他对象对自己的实例化,确保所有的对象都访问一个实例。
2.单例模式具有一定的伸缩性,类自己来控制实例化进程,这类就在改变实例化进程上具有相应的伸缩性。
3.提供了唯一实例的受控访问。
4.由于系统内存中只存在一个唯一实例,可以节约系统资源,当需要频繁的创建和销毁对象是,单例模式无疑可以提高系统性能。
5.允许可变数目的实例。
6.避免对共享资源的多重利用。
缺点:
1.不适用于变化的对象,如果同一类型的对象总是要在不同的场景中发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2.由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
3.单例类的职责过重,在一定程度上违背了“单一职责原则”。
4.滥用单例将带来一定的负面影响,为了节省资源将数据库连接池设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
适用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合下适用:
1.需要频繁实例化然后被销毁的对象。
2.创建对象时耗时过多或者消耗资源过多,但又经常使用的对象。
3.有状态的工具类对象。
4.频繁访问数据库或者文件的对象。
三.实现单例模式的要素和原则
1.确保一个类只有一个实例,自行实例化并向系统提供这个实例。
2.单例模式分类:
1)饿汉单例模式:类加载时实例化一个对象给自己的应用
2)懒汉单例模式:调用取得实例的方法如getInstance时才会实例化对象
3.单例模式要素:
1)私有构造方法
2)私有静态引用指向自己的实例
3)有自己的实例为返回值的公有静态方法
四.例子
1.饿汉单例,线程安全,但效率较低
public class Singleton{
/**私有化的构造方法,防止其他对象实例化该类*/
private Singleton(){}
/**将自身实例对象设置为自身类的属性,注意static和final修饰符*/
private static final Singleton instance = new Singleton();
/**静态方法返回该类的实例*/
public static Singleton getInstance(){
return instance;
}
}
2.懒汉单例,非线程安全
public class Singleton{
/**私有化的构造方法,防止其他对象实例化该类*/
private Singleton(){}
/**将自身实例对象设置为自身类的属性*/
private static Singleton instance;
/**静态方法返回该类的实例,只有在调用这个方法时才会实例化该类的对象*/
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
3.懒汉单例,线程安全简单实现
public class Singleton{
/**私有化的构造方法,防止其他对象实例化该类*/
private Singleton(){}
/**将自身实例对象设置为自身类的属性*/
private static Singleton instance;
/**
静态方法返回该类的实例,只有在调用这个方法时才会实例化该类的对象
注意synchronized修饰符
*/
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
4.线程安全,效率高,单例模式最优方案
public class Singleton{
/**私有化的构造方法,防止其他对象实例化该类*/
private Singleton(){}
/**
将自身实例对象设置为自身类的属性
不使用final关键字,使用volatile关键字,
保证了多线程访问时instance变量的可见性,
避免了instance初始化时其他变量属性还没赋值完时,
被另外线程调用
*/
private static volatile Singleton instance;
/**
静态方法返回该类的实例,只有在调用这个方法时才会实例化该类的对象
注意synchronized修饰符
*/
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
5.静态内部类模式
public class Singleton{
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
这种方式同样利用了classloader的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。