定义
单例模式(Singleton Pattern)使我们平时较为常用与简单的模式
定义:确保一个类只产生了一个实例,并且自行实例化,向整个系统提供这个实例
通用源码
//饿汉式
public class Singleton {
private static final Singleton singleton = new Singleton();
//限制类产生多个对象不能new
private Singleton() {
}
//只能通过这个方法来获得实例对象
public static Singleton getSingleton() {
return singleton;
}
//类中其他方法都尽量使用static
public static void doSomething() {
}
}
//懒汉式
public class Singleton {
private static final Singleton singleton = null;
//限制类产生多个对象不能new
private Singleton() {
}
//只能通过这个方法来获得实例对象
public static Singleton getSingleton() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
//类中其他方法都尽量使用static
public static void doSomething() {
}
}
通用源码如上图所示,Singleton类称为单例类,通过使用private关键字修饰的构造函数确保了在整个系统中不能通过new来产生实例,只能在Singleton中其自己使用new Singleton(),这样一个系统中也就只产生一个实例,且是自行实例化
应用
优点
- 单例模式在内存中只会存在一个实例,减少了内存开支,特别如果是一个要频繁创建、销毁的类,而创建、销毁时性能又无法优化,单例模式的效果就十分明显
- 单例模式只产生一个实例,就减小了系统的性能开销,当一个对象产生需要比较多的资源,如读取配置文件时,就可以通过在应用启动时直接产生一个单例对象,再永久驻留内存中的方式来解决
- 单例模式可以避免对资源的多重占用
- 单例模式可以在系统设置全局访问点,优化、共享资源访问
缺点
- 单例模式一般没有接口,扩展起来就比较困难,若要扩展只能进行修改代码
- 单例模式对于测试不方便
- 单例模式与单一职责原则有所冲突
使用场景
- 要求生成唯一序列号的场景
- 在整个项目中需要一个共享访问点或有共享数据时
- 创建一个实例需要消耗的资源过多(如要访问数据库、IO流)
- 需要定义大量的静态常量和静态方法的情况,可以使用单例模式(如工具类,也可以直接用static)
注意事项
使用单例模式时,要注意在高并发情况下的线程同步问题
在通用源码的示例代码中,可以看到单例模式的写法分为两种:饿汉式和懒汉式
饿汉式不需要担心而懒汉式就需要注意线程同步问题,懒汉式在获得实例对象时,需要先判断,这就会又一种情况发生:如果一个线程A执行到singleton = new singleton()
,但由于对象初始化需要时间,所以还没有获得对象,而这时另一个线程B也正在执行,并执行到if(singleton == null)
的判断,那这样线程B判断为真,于是进行下去,这样的话,线程A/B都获得了实例,内存中也就会产生多个实力
解决线程不安全的方法:在getSingleton方法前加synchronized关键字,但是这样也会影响性能,所以还是建议去使用饿汉式
总结
单例模式是较为简单的一种模式,也是使用较为广泛的一种模式,最熟知的比如在Spring中,每个Bean默认都是单例的