Java设计模式:单例模式

时间:2022-10-01 22:20:45

前言

以前对设计模式没有太大感觉。现在慢慢在工作中越来越感觉设计模式的重要性,所以决定重头来好好学习整理下常用的设计模式。
本文介绍的最初级的一个设计模式 单例模式

模式说明

java中单例模式定义:一个类有且仅有一个实例,并且自行实例化向整个系统提供。

应用: 一个系统中多线程对一个文件进行操作,这个文件就需要保证只有一个实例,等等

饿汉式

说明:饿汉式意思就是无论系统需不需要使用该类的实例对象,先在类加载的时候创建好
优点:实现简单
缺点:容易造成内存的浪费

常用饿汉式

该方式是在该类被加载的时候,就创建好了实例化对象。不允许其他地方通过构造方法来创建对象,只能通过提供的指定公共方法来获取唯一的对象。

public class SingletonDemo1 {

    //static 确保在类加载的时候就会初始化该对象
    private final static SingletonDemo1 singleton = new SingletonDemo1();

    // 必须是私有构造方法,防止其他地方通过构造方法来实例化对象
    private SingletonDemo1(){
    }

    // 是一个公共的静态方法,用于对外提供唯一的对象
    public static SingletonDemo1 getInstance(){
        return singleton;
    }

}

枚举

根据枚举的特性,可以使用枚举来实现单例模式

public class SingletonDemo3 {

    // 不允许其他地方通过构造方法进行实例化对象
    private SingletonDemo3(){}

    //对外提供可获取唯一实例化对象的公共方法
    public static SingletonDemo3 getInstance(){
        return Singleton.SINGLETON.getInstance();
    }

    private enum Singleton{
        SINGLETON;

        private SingletonDemo3 singleton;

        Singleton(){
            singleton = new SingletonDemo3();
        }

        public SingletonDemo3 getInstance(){
            return singleton;
        }
    }

}

懒汉式

说明:懒汉式意思就是在系统第一次需要使用该类的实例对象时,再创建该类的实例对象。
优点:不造成内存没必要的浪费
缺点:实现相对较难

懒汉式-线程不安全

在有多线程使用的情况下不要使用,否则可能会造成创建多个实例对象。一般建议不使用该种方式。

public class SingletonDemo1 {

    //static 确保在类加载的时候就会初始化该对象
    private static SingletonDemo1 singleton = null;

    // 必须是私有构造方法,防止其他地方通过构造方法来实例化对象
    private SingletonDemo1(){
    }

    // 是一个公共的静态方法,用于对外提供唯一的对象
    public static SingletonDemo1 getInstance(){
        if(singleton==null){
            singleton = new SingletonDemo1();
        }
        return singleton;
    }

}

懒汉式-线程安全-同步方法

由于上面的方式,是在执行getInstance()方法时,多线程使用时可能出现创建多个对象的情况。那可以直接将该方法进行同步,保证任意时间,最多是能一个线程使用该方法。

public class SingletonDemo2 {

    //static 确保在类加载的时候就会初始化该对象
    private static SingletonDemo2 singleton = null;

    // 必须是私有构造方法,防止其他地方通过构造方法来实例化对象
    private SingletonDemo2(){
    }

    // 是一个公共的静态方法,用于对外提供唯一的对象
    public static synchronized  SingletonDemo2 getInstance(){
        if(singleton==null){
            singleton = new SingletonDemo2();
        }
        return singleton;
    }

}

缺点:太重了,每个线程在每次使用的时候,都需要进行同步,而实际上只是在第一次获取该类的实例对象时需要同步,往后使用不需要同步。

懒汉式-线程安全-双重锁

该方式只会在未实例化对象时,才同步进行实例化对象。实例化后就不需要再进行同步了。

public class SingletonDemo2 {

    //static 确保在类加载的时候就会初始化该对象
    private static volatile SingletonDemo2 singleton = null;

    // 必须是私有构造方法,防止其他地方通过构造方法来实例化对象
    private SingletonDemo2(){
    }

    // 是一个公共的静态方法,用于对外提供唯一的对象
    public static SingletonDemo2 getInstance(){
        if(singleton==null){
            synchronized (SingletonDemo2.class){
                if(singleton==null) { //为了防止多个线程进行实例化
                    singleton = new SingletonDemo2();
                }
            }
        }
        return singleton;
    }

}

有么有注意到,使用了volatitle关键字,为什么要使用volatile关键字?
答:这里使用了volatile一个重要的特性——禁止JVM指令重排序
singleton = new SingletonDemo2()的在jvm内存中会有三步,1)分配内存;2)初始化对象;3)将对象指向分配的内存。jvm在会在不改变最终执行结果的情况下对指令进行重排序,即:可能执行顺序是1->3->2,若是在执行执行3还未执行2时,其他线程执行使用,则会报错。
(参考:https://www.cnblogs.com/crossoverJie/p/9326788.html

参考资料

https://www.cnblogs.com/paul011/p/8574650.html
http://www.runoob.com/design-pattern/singleton-pattern.html
java实例化对象过程说明:https://blog.csdn.net/justloveyou_/article/details/72466416