2.单例模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_32090861/article/details/88663101

简述:本篇文章主要是描述单例的一些关键点

1.构造函数用private

避免被其他类new出一个对象

2.懒汉式存在线程安全问题

public class Singleton2 {

    private Singleton2(){

    }

    private static Singleton2 instance;

    public static Singleton2 getInstance(){
        if(instance == null) {//1:读取instance的值
            instance = new Singleton2();//2: 实例化instance
        }
        return instance;
    }

}

原因:若A线程走到instance = new Singleton2();还没有new对象,B线程也进来,此时将会创建两个对象

解决:

a.在方法前面加synchronized修饰,这样肯定不会再有线程安全问题。

public class Singleton2 {
    private Singleton2(){
    }
    private static Singleton2 instance;
    public static synchronized Singleton2 getInstance(){
        if(instance == null) {//1
            instance = new Singleton2();//2
        }
        return instance;
    }
}

弊端:性能不够,每次执行getInstance都要先获取锁,有点串行化的感觉

b.加同步代码块,缩小粒度,此处可以使用双重检查锁的方式(synchronized代码块外层加一个判断,代码块的内部加一个判断:为什么代码块外部要再检查一次,因为会有多个线程在等待synchronized 代码块的锁,外部加一个判断可以过滤一部分,加快效率)

public static Singleton getSingleton() {
    if (instance == null) {                         //Single Checked
        synchronized (Singleton.class) {
         if (instance == null) {                 //Double Checked
                instance = new Singleton();
          }
    }
}
    return instance ;

c.指令重排序问题

instance = new Singleton()其实可以分为下面的步骤:

   (1).申请一块内存空间;

   (2).在这块空间里实例化对象;

   (3).instance的引用指向这块空间地址;

问题原因:对于以上步骤,指令重排序很有可能不是按上面123步骤依次执行的,有可能顺序:1.申请空间,3引用指向,2实例化对象;走到3,判断instance==null时,此时已经引用指向了,所以不会创建对象

解决:

加上volatile关键字,因为volatile可以禁止指令重排序。

public class Singleton2 {
    private Singleton2(){
    }
    private static volatile Singleton2 instance;
    public static Singleton2 getInstance(){
        if(instance == null) {
            synchronized (Singleton2.class){
                if (instance == null){
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}

3.避免被clone,单例类不可以实现cloneable接口,并实现clone方法,因为clone不需要调用构造函数即可复制对象

4.上限的多例模式,即在对象类中放一个list<Object>可以适当提升性能

public class Test{
    private static int maxNum=2;//最大对象数
    private static Vector<Object> list = new Vector<Object>();
    static{
        for(int i=0;i<maxNum;i++){
            list.add(new Test());
            }
        }
    private Test(){}
}

5.防止反射破坏单例 (定义常量,构造函数调用一次就+1,如果大于0就说明构造函数掉用了第二次,即第二次创建对象了)
 

public class Singleton {
    private static int count = 0;
 
    private static Singleton instance = null;
 
    private Singleton(){
        synchronized (Singleton.class) {
            if(count > 0){
                throw new RuntimeException("创建了两个实例");
            }
            count++;
        }
 
    }
 
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

6.使用单例最大的好处在于可以统一管理一个bean的生命周期,决定什么时候进行创建,什么时候销毁例如spring

猜你喜欢

转载自blog.csdn.net/qq_32090861/article/details/88663101