单例模式 顾名思义就是这个类只会创造一个唯一的实例。单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式又这样的一些特点
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
- 构造函数是private,从而避免了类在外部被实例化(其实可以使用反射来实例化,但是此处不进行考虑)
优点
- 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
- 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,比如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。
- 单例模式可以避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。
缺点
- 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
- 单例对象如果持有Context,那么很容易引发内存泄露,此时需要注意传给单例对象的Context最好是Application Context。
概括来说,单例模式有三种实现方式
- 懒汉式
- 饿汉式
- 枚举
懒汉式
懒汉的意思就是懒加载,直到使用的时候才创建。用的时候去检查该实例是否已经创建,如果创建的话则直接返回,如果没有创建的话则创建该实例。
优点
- 节省空间,使用的时候才创建实例
缺点
- 传统方式线程不安全
- 第一次调用需要做很多工作
线程不安全
public class Singleton{
private Singleton(){}
private static Singleton singleton=null;
public static Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
线程安全
线程安全式的懒汉模式有以下三种实现方式
synchronized
在getInstance()方法上加上synchronized关键字来保证线程安全。
这种方法虽然保证了线程安全,但是每次都需要同步锁定,会影响性能,毕竟99%的情况下都是不需要同步的。
public class Singleton{
private static Singleton singleton=null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
双重检查锁
在getInstance()中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,降低了每次都同步的性能损耗。
public class Singleton{
private static Singleton singleton=null;
private Singleton(){}
public static Singleton getInstance(){
if(singleton==null){
synchronized(Singleton.class){
if(singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
静态内部类
这种方式比上面的两种方式都更好,既保证了线程安全,又避免了同步带来的性能影响
public class Singleton{
private Singleton(){}
private static class SingletonHolder{
private static final Singleton INSTANCE=new Singleton();
}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
饿汉式
饿汉的意思就是,类加载的时候就已经初始化了单例。不管用没用到都先创建该实例,保证getInstance的时候,单例已经创建好了。
优点
- 天生线程安全
- 第一次调用比较快,因为之前已经创建了实例
缺点
- 浪费空间,不管使用还是没使用该实例,都创建了对象。
代码如下:
public class Singleton{
private Singleton(){}
private static final Singleton singleton=new Singleton();
public static Singleton getInstance(){
return singleton;
}
}
枚举
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
public enum Single{
INSTANCE;
public void anyMethod(){
}
}
总结
一般情况下,不建议使用线程不安全懒汉式和synchronized懒方式,建议使用饿汉式。只有在要明确实现 lazy loading 效果时,才会使用静态内部类。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检锁方式。