单例模式

 一、单例模式的概念

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。(维基百科)

 

二、单例模式的实现

1、懒汉式——需要时再点外卖

1.1 朴素版

public class Singleton{

    private static Singleton instance;
    
    //构造方法私有——禁止外部实例化
    private Singleton(){}

    //静态方法——外部通过类名访问
    public static Singleton getInstance(){

        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

说明:懒汉式在需要的时候再实例化对象,一定程度上节约内存

在多线程情况下不保证实例唯一(原因在于判断instance是否为null的时候有可能多个线程均进入了if条件块)

1.2 synchronized版

public class Singleton{

    private static Singleton instance;
    
    private Singleton(){}

    //同步方法——保证多线程下实例唯一
    public synchronized static Singleton getInstance(){

        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

说明:同步方法使得同一时刻只有一个线程进入方法块执行

但是实例化后每个想要进入该方法获取实例的线程都要阻塞排队(同步粒度粗——影响性能)

1.3 Double-Check版

public class Singleton{

    private volatile static Singleton instance;
       
    private Singleton(){}

    public static Singleton getInstance(){
        //保证实例化后线程不用进入同步块
        if(instance==null){
            //保证只有一个线程创建实例(若“多个线程”执行到此)
            synchronized(Singleton.class){
                //(使创建线程外的“多个线程”跳过)  
                if(instance==null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

说明:(Java虚拟机初始化一个对象总的来说,做了3件事情:1、在堆空间分配内存 2、执行构造方法进行初始化 3、将对象指向内存中分配的内存空间,也就是地址) 由于JVM和CPU在执行指令时有可能进行重排序,则可能在instance指向内存空间时对象没有初始化,通过引入volatile关键字解决这一问题。

1.4 静态内部类

public class Singleton{

    private Singleton(){}
   
    public static Singleton getInstance(){
        //访问时再加载
        return Single.instance;
    }

    private static class Single{
    
        private static Singleton instance = new Singleton();
    }
}

说明:懒汉式,线程安全

 

2、饿汉式——提前点好外卖

public class Singleton{
    //类加载时实例化
    private static Singleton instance = new Singleton();  
    
    private Singleton(){}

    public static Singleton getInstance(){

        return instance;
    }
}

说明:由于提前加载实例,在一定程度上会占用内存

饿汉式是在类加载时实例化(虚拟机保证每个类只加载一次),因此不会出现多线程问题

 

三、单例模式的应用

       Spring中的Bean默认即为单例、Windows的Task Manager(任务管理器)

       网站的计数器、数据库连接池、多线程的线程池

 

四、单例の其他问题

1、枚举实现单例

***枚举是特殊的常量类,且构造方法被默认强制私有***

public enum Singleton{
    INSTANCE;
}

  • 枚举类实现其实省略了private类型的构造函数
  • 枚举类的域(field)其实是相应的enum类型的一个实例对象
public enum Singleton {
    INSTANCE;
    //隐藏的私有构造方法
    private Singleton () {}
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        //输出Singleton,instance是该类的实例
        System.out.println(instance.getClass().getName());
    }
}
  • 枚举单例的常见写法
// 定义单例模式中需要完成的代码逻辑
public interface MySingleton {
    void do();
}
public enum Singleton implements MySingleton {
    INSTANCE {
        @Override
        public void do() {
            //TODO
        }
    };
    public static MySingleton getInstance() {
        return Singleton.INSTANCE;
    }
}

2、单例的破坏

  • 序列化

      如果过该类实现Serializable,那么就会在反序列化的过程中再创建一个对象

      这个问题的解决办法就是在反序列化时,指定反序化的对象实例

private static Singleton instance = new Singleton();

    private Object readResolve() {
        return instance;
    }
  • 反射

       反射可以获取类的构造函数,通过setAccessible(true)方法可以调用私有的构造函数,创建对象

  

五、Reference

维基百科

深入解析单例模式的七种实现

单例模式的应用

枚举的用法

单例的破坏

 

六、Resource


       大话设计模式

       Head First 设计模式

       Java设计模式

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值