java设计模式--单例模式(饿汉、懒汉、双重检索)-附代码

目录(其他设计模式请移步该文章进行查看,持续更新,所有的代码都在码云上,可自行下载):

java常用设计模式以及场景使用及代码实现-系列_言之有李LAX的博客-CSDN博客

【释义】

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

单例在实现方式上主要有:饿汉模式、懒汉模式(线程不安全)、懒汉模式(线程安全)、双重检索模式(推荐使用)以及其他不列举。

【场景】

单例在项目中的使用场景一般是针对频繁创建和销毁的对象,需根据业务自行判断。比如Spring中的bean创建、数据库的连接池等。优点是保证了对象的实例有且只有一个,节省资源,获取速度较快。缺点自然也有,就是对于经常变化的对象不适用。

【实现方式】

 tips: 四种模式中推荐使用第四种,第一种其次,但是需要从1-4进行观看更容易理解!

1. 饿汉模式

【释义】-就是比较饥饿,在类实例化的时候就要创建实例,无论后面是否会真正用到。

【优点】-线程安全,在类加载的同时已经创建好一个静态对象,调用的时候反应速度快

【缺点】-对象提前创建,如果后续业务没有使用到该实例,就会造成内存浪费,虽然现在的服务内存都比较大,但是如果项目中存在很多饿汉单例,也不容小觑。相当于用空间换时间。

public class EhSingleton {
    /**
     * 私有化该实例,且静态变量在类加载的时候就会初始化,所以是线程安全的
     */
    private static final EhSingleton ehSingleton = new EhSingleton();

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

    }

    /**
     * 该类对外提供的唯一获取实例方法(静态工厂方法)
     *
     * @return
     */
    public static EhSingleton getInstance() {
        return ehSingleton;
    }
    
    /**
     * 运行main方法,可以在控制台看到三条打印的对象实际是同一个
     * 实现了单例保证系统中只有一个实例的结果
     */
    public static void main(String[] args) {
        EhSingleton ehSingleton = EhSingleton.getInstance();
        System.out.println(ehSingleton);
        EhSingleton ehSingleton1 = EhSingleton.getInstance();
        System.out.println(ehSingleton1);
        EhSingleton ehSingleton2 = EhSingleton.getInstance();
        System.out.println(ehSingleton2);
    }

}

运行该类中的main方法,可以在控制台看到三条打印的对象实际是同一个, 实现了单例保证系统中只有一个实例的结果

2.懒汉模式(非线程安全)

 【释义】-就是比较懒,只有在真正被调用的时候才会去检查有没有实例,如果有则直接返回,如果没有则会新建,然后返回。

【优点】-有延迟加载的效果,但是只能在单线程环境下使用

【缺点】-如果在多线程的环境下,两个线程同时进入到了(lhSingleton == null) 的判断中,则两个线程都会得到lhSingleton == null,  从而这两个线程都会进入到if语句中进行创建对象,便会产生多个实例。

public class LhUnsafeSingleton {

    /**
     * 私有化实例
     */
    private static LhUnsafeSingleton lhUnsafeSingleton;

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

    }

    /**
     * 该类对外提供的唯一获取实例方法(静态工厂方法)
     *
     * @return
     */
    public static LhUnsafeSingleton getInstance() {
        if (lhUnsafeSingleton == null) {
            lhUnsafeSingleton = new LhUnsafeSingleton();
        }
        return lhUnsafeSingleton;
    }


    public static void main(String[] args) {
        //非线程安全,不好复现  可以通过写多线程同时请求LhUnsafeSingleton.getInstance() 进行模拟,并打印出来返回的结果
        //如果返回的结果有不同的,则表示懒汉模式在多线程下容易出问题
    }

}

3.懒汉模式(线程安全)

【释义】-就是比较懒,只有在真正被调用的时候才会去检查有没有实例,如果有则直接返回,如果没有则会新建,然后返回, 同时对getInstance()方法添加synchronized关键字

【优点】-解决了懒汉模式线程不安全的问题

【缺点】-每次调用都加锁同步执行,对象返回的效率低,不推荐使用

public class LhSafeSingleton {

    /**
     * 私有化实例
     */
    private static LhSafeSingleton lhSafeSingleton;

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

    }

    /**
     * 该类对外提供的唯一获取实例方法(静态工厂方法) 该方法使用synchronized加锁,来保证线程安全性
     * synchronized作用在方法上,如果有别的线程来访问该方法,需要先获取锁,查看对象是否可用
     *
     * @return
     */
    public static synchronized LhSafeSingleton getInstance() {
        if (lhSafeSingleton == null) {
            lhSafeSingleton = new LhSafeSingleton();
        }
        return lhSafeSingleton;
    }
}

4. 双重检索模式

进入方法先检查实例是否存在,存在则直接返回,如果不存在才会进入同步代码,  此时再次检查是否存在实例,如果不存在,则在同步状态下进行实例创建,既保证了懒加载,又保证了高性能。 项目中常用此种单例模式。

public class DoubleCheckSingleton {

    /**
     * 私有化实例
     */
    private static DoubleCheckSingleton doubleCheckSingleton;

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

    }

    /**
     * 该类对外提供的唯一获取实例方法(静态工厂方法)
     *
     * @return
     */
    public static DoubleCheckSingleton getInstance() {
        //首先判断是否为空,防止已经被实例化后再次加锁,导致重复操作
        if (doubleCheckSingleton == null) {
            //加锁
            synchronized (DoubleCheckSingleton.class) {
                //此时已经加锁,同一时间只有一个线程会进入此步骤,所以不会出现懒汉模式时的问题
                if (doubleCheckSingleton == null) {
                    doubleCheckSingleton = new DoubleCheckSingleton();
                }
            }
        }
        return doubleCheckSingleton;
    }

}

当然还有其他方式比如枚举等模式,但是基本不会用到,就不一一列举了,感兴趣的可以去自行搜一下。

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