单例设计模式的几种写法以及优缺点

一、什么是单例设计模式

	在写单例设计模式前,首先需要了解什么是单例设计模式:单例模式,指的是一个类只有一个实例,并且有一个全局可以访问的接口。他有以
下几个优点:
	(1)可以节省内存和计算,很多时候我们其实只需要一个实例就够了,多个实例会造成浪费,比如对于一些初始化比较耗时的类,就只用生成
一个实例保存在内存中供大家一起实用,没有必要多次生成新的实例。
	(2)单例设计模式可以保证全局计算结果的正确,比如一个全局计数器用于统计人数,如果有多个计数器反而会造成混乱。
	(3)方便管理,对于很多工具类,我们生成一个实例,然后通过一个统一的入口来获取这个单例,太多实例反而会眼花缭乱。

二、单例设计模式适用场景

  1. 无状态的工具类,如日志工具类,我们不需要他来帮我们存储,只需要他来记录日志信息,这时候只需要一个实例就可以了。
  2. 全局信息类,如环境变量类,比如统计一个网站的浏览量,可以让这个类变成一个单例,在需要计数的时候拿出来用就可以了。

三、常见的单例模式写法

1. 饿汉式

public class SingletonEH {

    private static SingletonEH singletonEH = new SingletonEH();
    
    private SingletonEH(){};
    
    public static SingletonEH getInstance(){
        return singletonEH;
    }
}

特点:在类装载的时候完成实例化,避免了线程同步问题
缺点:如果从始至终没有使用到该实例,会造成内存浪费

2. 懒汉式

线程不安全的写法

public class SingletonLH {

    private static SingletonLH singletonLH;

    private SingletonLH(){}

    public static SingletonLH getInstance(){
        if (singletonLH == null){
            singletonLH = new SingletonLH();
        }
        return singletonLH;
    }
}

特点:起到了懒加载的作用,在调用getInstance()方法后才去实例化对象。
缺点:只能在单线程下使用。多线程情况下,如果一个线程进入了if (singletonLH == null)判断,还没有实例化的时候,另一个线程也进入了判断语句,这时候就会多次创建实例。

线程安全的写法

public class SingletonLH {

    private static SingletonLH singletonLH;

    private SingletonLH(){}

    public static synchronized SingletonLH getInstance(){
        if (singletonLH == null){
            singletonLH = new SingletonLH();
        }
        return singletonLH;
    }
}

缺点:效率太低,为了让懒汉式线程安全且效率高,提出了双重检查式

3. 双重检查式

public class SingletonLH {

    private static volatile SingletonLH singletonLH;

    private SingletonLH(){}

    public static SingletonLH getInstance(){
        if (singletonLH == null){
            synchronized (SingletonLH.class){
                if (singletonLH == null){
                    singletonLH = new SingletonLH();
                }
            }
        }
        return singletonLH;
    }
}

特点:线程安全,效率高
缺点:无法防止被反序列化生成多个实例

两个判断如果去掉第一个,会导致所有线程串行执行,效率低下。如果去掉第二个,可能会出现多个线程同时进入判断,从而产生多个实例。并没有保证线程安全,因此两个判断缺一不可。

需要注意的地方是volatile来修饰了变量。它能有效防止重排序的发生,保证持有同一个对象锁的两个同步块只能串行执行,这样可以避免可能存在的一种情况:线程一先指向单例对象的内存地址(此时对象已经不是null了),但是还未初始化,这时候线程二进来了,且通过了是否为null的判断,将还未初始化的对象返回使用。这种情况会报错

4. 静态内部类式

public class SingletonStatic {

    private SingletonStatic(){}

    private static class SingletonStaticInstance{
        private static final SingletonStatic singletonStatic = new SingletonStatic();
    }

    public static SingletonStatic getInstance(){
        return SingletonStaticInstance.singletonStatic;
    }

}

特点:跟饿汉式类似,采用类装载的时候实例化对象,区别在于饿汉式是在类加载的时候实例化,静态内部类式是在调用了getInstance()方法的时候才会去完成对该实例的实例化,和双重检查式一样,线程安全且效率高
缺点:无法防止被反序列化生成多个实例

5. 枚举式(推荐)

public enum SingletonEnum {
    INSTANCE;
    public void whateverMethod(){
        
    }
}

优点:简洁,线程安全,能防止序列化和反射创建多个实例从而破坏单例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值