设计模式之单例模式的六种写法

单例模式,指的是采取一定的方法,保证在整个软件系统中,对某个类来说,它只存在一个实例对象。

说起单例模式,我们一般都能说出饿汉式和懒汉式。本文提供具体的六个单例模式的实现方式,并讨论它们的优缺点。

一、饿汉式

首先,最简单的实现方式就是饿汉式。

  1. 在类中定义私有静态属性,并且进行初始化
  2. 构造器私有化
  3. 提供一个静态公共方法,在需要时能获得该实例对象。

根据初始化的位置不同,又可以分为两种写法:显式初始化和静态代码块初始化。

饿汉式在类加载的时候就完成了实例化,避免了线程同步的问题,但是也没有达到懒加载的效果,因此如果自始至终没有用到过这个实例,有内存浪费的可能。

public class Singleton {
    //私有静态属性,显式初始化
    private final static Singleton instance = new Singleton();
    //构造器私有化
    private Singleton() {}
    //向外提供一个静态的公共方法,返回实例对象instance
    public static Singleton getInstance() {
        return instance;
    }
}
public class Singleton {
    //私有静态对象
    private final static Singleton instance;
    //构造器私有化
    private Singleton() {}
    //静态代码块初始化
    static {
        instance = new Singleton();
    }
    //向外提供一个静态的公共方法,返回实例对象instance
    public static Singleton getInstance() {
        return instance;
    }
}

二、懒汉式

为了实现懒加载,我们来看看懒汉式。懒汉式与饿汉式的不同在于给私有静态属性初始化的时机不同。我们选择在需要的时候再进行初始化,得到实例对象。

public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            //①
            instance = new Singleton();
        }
        return instance;
    }
}

上述写法实现了懒加载,但是又存在线程不安全的问题。当多个线程都进入①,会创建多个实例,不满足单例模式的要求。因此,我们做出改进如下

同步方法:将获取实例的方法定义为synchronized。这样虽然解决了线程安全问题,但是当多个线程想要获得实例的时候都要进行同步,即使实例已存在。我们希望当实例已经被创建后,其他线程访问时可以直接获得而不要全部等待,我们希望提高效率。

public class Singleton {
    private static Singleton instance;
    private Singleton() {}
    //静态方法:同步监视器是类本身
    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

双重检查+同步代码块法:线程想要获取实例对象时,先判断其是否已存在,若存在,直接返回;若不存在,试图创建实例对象,即进入①。此时若有多个线程进行同步,在同步代码块内还需再进行一次判断,判断实例是否已被创建,这就保证了自始至终无论存在多少线程想要创建实例对象,都只会创建一次。因此,该方法既保证了线程安全,又实现了加载,而且效率高。

public class Singleton {
    //volatile:让修改值立即更新到内存
    private static volatile Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if (instance==null){
            //①
            synchronized (Singleton.class){
                if (instance==null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

三、静态内部类法

唯一的实例对象作为内部类的私有静态属性而存在。当加载外部类的时候,内部类不会加载,以实现懒加载;需要获取实例的时候,加载内部类,此时利用JVM底层类加载机制,保证了内部类成员变量初始化的线程安全。

public class Singleton {
    private Singleton() {}
    //1. 加载外部类的时候,内部类不会加载,实现懒加载
    private static class SingletonInstance {
        private final static Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){
        //2. 此时加载内部类,利用JVM的底层装在机制:在加载类的时候是线程安全的
        return SingletonInstance.INSTANCE;
    }
}

四、枚举类法

避免线程同步问题,防止反序列化重新创建新的对象

public enum Singleton {
    INSTANCE;
}

优缺点对比

1. 饿汉式:显式初始化、静态代码块初始化

线程安全,但是没有实现懒加载。

2. 懒汉式

同步方法

线程安全,实现懒加载,但是效率低。

双重检查+同步代码块法

线程安全,实现懒加载,效率高。

3. 静态内部类法

线程安全,懒加载。

4. 枚举类法

线程安全,防止反序列化重新创建新的对象

总结

推荐使用

  1. 懒汉式中双重检查+同步代码块法
  2. 静态内部类法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值