java设计模式之单例模式

一、什么是单例模式

单例模式顾名思义就是任何适合都只能有一个实例。且该类需自行创建这个实例,并对其他的类提供调用这一实例的方法。是java中常用的设计模式。
java设计模式之单例模式_第1张图片

二、单例的几种实现方式

1. 饿汉模式
饿汉式,就是“比较饿”,实例在初始化的时候就要创建好,不管你有没有用到。

优点:线程安全;在类加载(ClassLoader)的同时已经创建好一个静态对象,调用时反应速度快。
缺点:对象提前创建,所以会占据一定的内存,内存占用大 以空间换时间。

/**
 * @author lucia
 * @date 2022/6/23 13:52
 */
public class Singleton {

    /**
     * 私有实例,静态变量会在类加载的时候初始化,是线程安全的
     */
    private static final Singleton instance = new Singleton();

    /**
     * 私有构造方法
     */
    private Singleton() {
    }

    /**
     * 唯一公开获取实例的方法(静态工厂方法)
     *
     * @return
     */
    public static Singleton getInstance() {
        return instance;
    }
}

2. 懒汉模式(线程不安全)
懒汉式就是“比较懒”,就是在用到的时候才去检查有没有实例,如果有则直接返回,没有则新建。

优点:起到了Lazy Loading的效果,但是只能在单线程下使用。
缺点:如果在多线程下,两个线程同时进入了if (singleton == null)判断语句块,这时便会产生多个实例。所以
在多线程环境下不可使用这种方式。

/**
 * @author lucia
 * @date 2022/6/23 13:52
 */
public class Singleton {

    /**
     * 私有实例
     */
    private static Singleton instance;

    /**
     * 私有构造方法
     */
    private Singleton() {
    }

    /**
     * 唯一公开获取实例的方法(静态工厂方法),
     *
     * @return
     */
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

3.懒汉模式(线程安全)
对getInstance()方法添加synchronized关键字
优点:解决了线程不安全问题
缺点:每次调用都加锁同步执行,对象返回效率低,不推荐使用。

/**
 * @author lucia
 * @date 2022/6/23 13:52
 */
public class Singleton {

    /**
     * 私有实例
     */
    private static Singleton instance;

    /**
     * 私有构造方法
     */
    private Singleton() {
    }

    /**
     * 唯一公开获取实例的方法(静态工厂方法),该方法使用synchronized加锁,来保证线程安全性
     *
     * @return
     */
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

4.双重检索模式
进入getInstance方法时先不同步,进入方法过后,先检查实例是否存在,如果存在则直接返回,如果不存在才进入synchronized修饰的同步代码块,进入后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例。即保证了懒加载,又保证了高性能。推荐使用。

双重检查加锁机制的实现使用到一个关键字volatile:被volatile修饰的变量的值,保证了多个线程之间的可见性。即被一个线程修改后,其他线程立即可见。

/**
 * @author lucia
 * @date 2022/6/23 13:52
 */
public class Singleton {

    /**
     * 私有实例 volatile
     */
    private volatile static Singleton instance;

    /**
     * 私有构造方法
     */
    private Singleton() {
    }

    /**
     * 唯一公开获取实例的方法(静态工厂方法)
     *
     * @return
     */
    public static Singleton getUniqueInstance() {
        //第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作
        if (instance == null) {
            // 加锁
            synchronized (Singleton.class) {
                //第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出现 instance == null 时两个线程同时进行实例化操作。
                if (instance == null) { 
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}


5.静态内部类
该模式利用了静态内部类延迟初始化的特性,来达到与双重校验锁方式一样的功能。
缺点: 可以被反射破坏

/**
 * @author lucia
 * @date 2022/6/23 13:52
 */
public class StaticInnerClassSingleton {

    private StaticInnerClassSingleton(){}


    /**
     * 唯一公开获取实例的方法(静态工厂方法)
     *
     * @return
     */
    public static StaticInnerClassSingleton getInstance() {
        return LazyHolder.INSTANCE;
    }

    /**
     * 私有静态内部类
     */
    private static class LazyHolder {
        private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }
}

反射破坏静态内部类单例模式附上代码:

public static void main(String[] args) {

        try {
            Constructor<StaticInnerClassSingleton> con = StaticInnerClassSingleton.class.getDeclaredConstructor();
            con.setAccessible(true);
            StaticInnerClassSingleton singleton = (StaticInnerClassSingleton) con.newInstance();
            System.out.println(singleton);

            StaticInnerClassSingleton singleton5 = (StaticInnerClassSingleton) con.newInstance();
            System.out.println(singleton5);

            StaticInnerClassSingleton singleton6 = (StaticInnerClassSingleton) con.newInstance();
            System.out.println(singleton6);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

运行结果:

6.枚举
优点:枚举式单例不会被反射破坏 , 内存中只存在一个对象实例,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。

缺点:当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰。

public enum Singleton {

    INSTANCE;

    public void method() {
        System.out.println("枚举类中定义方法!");
    }

}

你可能感兴趣的:(java设计模式,单例模式,java,开发语言)