[设计模式] 单例模式(singleTon)

单例模式 - SingleTon

【介绍】单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为。这种不能自由构造对象的情况,就是单例模式的使用场景。

【定义】确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

【使用场景】确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,比如访问IO、数据库等资源。

【关键点】
①构造函数不对外开放,一般为Private;
②通过一个静态方法或者枚举返回单例类对象;
③确保单例类的对象有且只有一个,尤其在多线程环境下;
④确保单例类对象在反序列化时不会重新构建对象。

通过将单例类的构造函数私有化,使得客户端代码不能通过new的形式手动构造单例类的对象。

单例类会暴露一个公有静态方法,客户端需要调用这个静态方法获取到单例类的唯一对象,在获取这个单例对象的过程中需要确保线程安全(在多线程环境下构造单例类的对象也是有且只有一个)。

【实现方式】

A. 饿汉式

// 单例模式 - 饿汉式
public class SingleTon {
    // 在声明静态对象时就对其初始化
    private static final SingleTon instance = new SingleTon();

    // 构造函数私有
    private SingleTon(){
    }

    // 公有的对外静态方法,对外暴露获取单例对象的接口,等用户调用该方法时直接返回已经实例化的单例对象
    public static  SingleTon getInstance(){
        return instance;
    }
}
public class Test {
    public static void main(String[] args){
        // SingleTon对象只能通过对外暴露获取单例对象的公共方法“getInstance()”获取到
        SingleTon singleTon = SingleTon.getInstance();
    }
}

B. 懒汉式

// 单例模式 - 懒汉式
public class SingleTon{
    // 声明一个静态对象先不实例化,等用户第一次调用公共方法“getInstance()”时再进行实例化
    private static SingleTon instance;

    // 构造函数私有
    private SingleTon(){}

    // 公有的对外静态方法,对外暴露获取单例对象的接口,等用户第一次调用该方法时再进行实例化
    // 【同步方法】可在多线程情况下保证单例对象的唯一性。
    public static synchronized SingleTon getInstance(){
        if (instance == null){
            instance = new SingleTon();
        }
        return instance;
    }
}

小结:
懒汉优点:单例只有在使用时才会被初始化,在一定程度上节约了资源;
懒汉缺点:第一次加载时需要进行初始化,反应较慢;
最大问题:每次调用“getInstance()”方法时都进行同步,造成不必要的同步开销。
因此这种模式一般不建议使用。

C. Double Check Lock (DCL)

// 单例模式 - Double Check Lock (DCL)
public class SingleTon{
    // 声明一个null静态对象先不实例化,等用户第一次调用公共方法“getInstance()”时再进行实例化
    private static SingleTon sInstance = null;

    // 构造函数私有
    private SingleTon(){}

    // 公有的对外静态方法,对外暴露获取单例对象的接口,等用户第一次调用该方法时再进行实例化
    // 两层判空是应为涉及到java编译器编译时的问题。
    public static SingleTon getInstance(){
        // 第一层判空是为了避免不必要的同步
        if (mInstance == null){
            synchronized (SingleTon.class){
                // 第二层判空是为了在null的情况下创建实例
                if (mInstance == null){
                    sInstance = new SingleTon();
                }
            }
        }
        return sInstance;
    }

    public void doSomething(){
        System.out.println("do something..");
    }
}

小结:
优化:优化了懒汉方式下过多的同步开销;
优点:资源利用率高,第一次执行“getInstance()”方法时单例对象才会实例化,又能保证线程安全,且不会产生不必要的同步锁;
缺点:第一次加载时反应稍微慢,偶尔会失败,在高并发环境下也有小概率的缺陷。
DCL方式是使用最多的单例实现方式,除非你的并发场景比较复杂或者低于JDK 6版本。

D. 静态内部类

// 单例模式 - 静态内部类
public class SingleTon{
    // 构造函数私有
    private SingleTon(){}

    // 公有的对外静态方法,对外暴露获取单例对象的接口
    // 【优化】第一次加载“SingleTon类”时不会初始化instance对象,只有等用户第一次调用该方法时才会导致虚拟机加载“内部类SingleTonHolder”导致instance被初始化
    public static SingleTon getInstance(){
        return SingleTonHolder.instance;
    }
    
    // 静态内部类 - 加载该类才会导致instance被初始化。①保证线程安全;②保证单例对象唯一;③延迟单例的实例化
    private static class SingleTonHolder{
        private static final SingleTon instance = new SingleTon();
    }
}

小结:
优化:优化了DCL方式在某些情况下失效(双重检查锁定失效)的问题;
优点:①保证线程安全;②保证单例对象唯一;③延迟单例的实例化;
因此,该方式是值得推荐的单例模式实现方式。

E. 枚举

// 单例模式 - 枚举
public enum SingleTon{
    // 枚举在java中不仅可以拥有字段
    INSTANCE;
    // 枚举在java中还可以拥有自己的方法
    public void doSomething(){
        System.out.println("do something..");
    }
}

小结:
优化:优化了上述几种单例实现方式在反序列化的情况下会重新创建对象;
优点:①写法超级简单;②线程安全;③任何情况下它都是一个单例;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值