Loading

单例设计模式

概念理解

 单例设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

 该设计模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,并且由这个类提供一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象。

 单例设计模式分为:懒汉式饿汉式

饿汉式:类加载会导致该单实例对象被创建。
懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会被创建。

饿汉式

静态变量方式

public class Singleton {
    private Singleton(){};
    private static Singleton singleton = new Singleton();
    public static Singleton getSingleton(){
        return singleton;
    }
}

静态代码块方式

public class Singleton {
    private Singleton(){};
    private static  Singleton instance;
    static {
        instance = new Singleton();
    }
    public static Singleton getInstance(){
        return instance;
    }
}

枚举方式

 枚举类型是线程安全的,并且只会装载一次。枚举类型是所有单例实现中唯一一种不会被破坏的单例实现模式。

public enum Singleton {
    INSTANCE;
}

懒汉式

线程不安全方式

public class Singleton {
    private Singleton(){};
    private static Singleton instance;
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

线程安全方式

 在 getInstance() 方法上添加synchronized关键字,解决线程安全问题,但是导致该方法的执行效果特别低。

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

双重检查锁方式

 解决了单例、性能、线程安全问题,但是在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
 为解决双重检查锁模式带来的空指针异常问题,只需要使用volatile关键字,volatile关键字可以保证可见性和有序性。能够保证在多线程的情况下线程安全也不会有性能问题。

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

静态内部类方式

 静态内部类单例模式中实例由内部类创建,由于JVM在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被static修饰,保证只被实例化一次,并且严格保证实例化顺序。
 静态内部类方式保证了多线程下的安全,并没有任何性能影响和空间的浪费。

public class Singleton {
    private Singleton(){}
    private static class SingletonHolder{
         private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

存在问题与解决方案

 除了枚举方式,如果将Singleton序列化或者反射,则可以创建多个对象。

序列化问题

 下面代码演示表明,序列化和反序列化已经破坏了单例模式。

public class Singleton implements Serializable {
    private Singleton(){}
    private static class SingletonHolder{
         private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

public class SerializableQuestion {
    public static void main(String[] args) throws Exception {
       //write();
        Singleton singleton1 = read();
        Singleton singleton2 = read();
        System.out.println(singleton1 == singleton2);
//输出结果为false

    }
    public static Singleton read() throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\upload\\object.txt"));
        Singleton instance = (Singleton) ois.readObject();
        return instance;

    }
    public static void write() throws IOException {
        Singleton instance = Singleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\upload\\object.txt"));
        oos.writeObject(instance);
        oos.close();
    }
}

序列化问题解决

 在Singleton类中添加readResolve()方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。

public class Singleton implements Serializable {
    private Singleton(){}
    private static class SingletonHolder{
         private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
    private Object readResolve(){
        return SingletonHolder.INSTANCE;
    }
}

反射问题

public class Singleton {
    private Singleton(){}
    private static class SingletonHolder{
         private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

public class ReflectionQuestion {
    public static void main(String[] args) throws Exception {
        //获取Singleton的字节码对象
        Class singletonClass = Singleton.class;
        //获取无参构造函数
        Constructor cons = singletonClass.getDeclaredConstructor();
        //取消访问检查
        cons.setAccessible(true);
        Singleton s1 = (Singleton) cons.newInstance();
        Singleton s2 = (Singleton) cons.newInstance();
        System.out.println(s1 == s2);
    }

反射问题解决

 调用构造方法进行创建时,直接抛出异常。

public class Singleton {
    private static boolean flag = false;
    private Singleton(){
        //非第一次访问
        if(flag){
            throw new RuntimeException("不可以创建多个对象!");
        }
        flag = true;
    }
    private static class SingletonHolder{
         private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}
posted @ 2021-09-08 09:53  IamHzc  阅读(31)  评论(0编辑  收藏  举报