几种常见的单例模式学习

懒汉式(线程不安全)

//只适合单线程环境
public class Singleton {
    private static Singleton instance=null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}
  • 注解:Singleton的静态属性instance中,只有instance为null的时候才创建一个实例,构造函数私有,确保每次都只创建一个,避免重复创建。
  • 缺点:只在单线程的情况下正常运行,在多线程的情况下,就会出问题。例如:当两个线程同时运行到判断instance是否为空的if语句,并且instance确实没有创建好时,那么两个线程都会创建一个实例。

懒汉式(线程安全)

//多线程的情况可以用
public class Singleton {
    private static Singleton instance=null;
    private Singleton(){}
    public static synchronized Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}
  • 注解:在解法一的基础上加上了同步锁,使得在多线程的情况下可以用。例如:当两个线程同时想创建实例,由于在一个时刻只有一个线程能得到同步锁,当第一个线程加上锁以后,第二个线程只能等待。第一个线程发现实例没有创建,创建之。第一个线程释放同步锁,第二个线程才可以加上同步锁,执行下面的代码。由于第一个线程已经创建了实例,所以第二个线程不需要创建实例。保证在多线程的环境下也只有一个实例。
  • 缺点:每次通过getInstance方法得到singleton实例的时候都有一个试图去获取同步锁的过程。而众所周知,加锁是很耗时的。能避免则避免。

懒汉式双重校验锁(可行)

public class Singleton {
    private static Singleton instance=null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance==null){
            synchronized(Singleton.class){
                if(instance==null){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 注解:只有当instance为null时,需要获取同步锁,创建一次实例。当实例被创建,则无需试图加锁。
  • 缺点:用双重if判断,复杂,容易出错。

懒汉式双重校验锁升级(volatile修饰符)

public class Singleton { 

    // 定义一个私有构造方法
    private Singleton() {}   
    //定义一个静态私有变量(不初始化,不使用final关键字,
    //使用volatile保证了多线程访问时instance变量的可见性,避免了instance初始化时其他变量属性还没赋值完时,被另外线程调用)
    private static volatile Singleton instance = null;  

    //定义一个共有的静态方法,返回该类型实例
    public static Singleton getIstance() { 
        // 对象实例化时与否判断(不使用同步代码块,instance不等于null时,直接返回对象,提高运行效率)
        if (instance == null) {
            //同步代码块(对象未初始化时,使用同步代码块,保证多线程访问时对象在第一次创建后,不再重复被创建)
            synchronized (Singleton.class) {
                //未初始化,则初始instance变量
                if (instance == null) {
                    instance = new Singleton();   
                }   
            }   
        }   
        return instance;   
    }   
}

饿汉式(建议使用)

public class Singleton {
    private static final Singleton instance=new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}
  • 注解:初试化静态的instance创建一次。如果我们在Singleton类里面写一个静态的方法不需要创建实例,它仍然会早早的创建一次实例。而降低内存的使用率。 饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
  • 缺点:没有lazy loading的效果,从而降低内存的使用率。

静态内部类(建议使用)

public class Singleton {
    private Singleton(){}
    private static class SingletonHolder{
        private final static Singleton instance=new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}
  • 注解:定义一个私有的内部类,在第一次用这个嵌套类时,会创建一个实例。而类型为SingletonHolder的类,只有在Singleton.getInstance()中调用,由于私有的属性,他人无法使用SingleHolder,不调用Singleton.getInstance()就不会创建实例。
  • 优点:达到了lazy loading(懒加载)的效果,即按需创建实例。

枚举

public enum SingletonEnum {  
    /** 
     * 1.从Java1.5开始支持; 
     * 2.无偿提供序列化机制; 
     * 3.绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候; 
     */  
    instance;  
    private String others;  
    SingletonEnum() {  

       } 
    }
  • 这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象

适用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
1.需要频繁实例化然后销毁的对象。
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3.有状态的工具类对象。
4.频繁访问数据库或文件的对象。
以下都是单例模式的经典使用场景:
1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2.控制资源的情况下,方便资源之间的互相通信。如线程池等。

使用单例时注意造成内存泄露
如:

public class Singleton {
    private static Singleton instance ;
    private Context mContext;
    private Singleton(Context context){
		this.mContext = context; //如果传入的是Activity 当此Activity退出时,Activity应该被回收,可是这个单例持有这个activity的强引用,导致回收失败,所以造成内存泄漏。
		//改为 使用Application的context
		this.mContext = context.getApplicationContext(); 
		//Application的contex不是万能的 则可以考虑弱引用,AlertDialog创建就不能用Application的contex
		WeakReference contextWeakReference = new WeakReference<>(context);
        this.mContext = contextWeakReference.get();
	}
    public static Singleton getInstance(Context context){
    	if(instance = null){
			instance  = new Singleton (context)
		}
        return instance;
    }
}
发布了27 篇原创文章 · 获赞 187 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/Mr_wzc/article/details/70248005