程序员:我终于知道单例模式是什么了

483 阅读6分钟

作者:千珏

邮箱:wl625363199@gmail.com

公众号:千珏(jue)

标题:设计模式之单例模式(转载请标明出处)

​   Hello,各位看官老爷们,千珏我又回来各位更新了,如果有关注我动态的人应该知道我在年前发了一个flag说要在过年假期的时候把设计模式全部写完,结果都已经三月了我还没写完,看来我真是一个合格的鸽子呢[手动狗头]

​   这几天我一直在想怎么写这些设计模式呢,因为网上关于设计模式的轮子太多了,我们一直提倡的是用轮子,不要重复创造轮子,网上的设计模式教程虽多,但是千珏觉得网上那些写的有点太麻烦了,所以就想着自己在创造一遍轮子。

​   好了不废话了,下面开始进入正文,这期千珏讲的就是设计模式中的单例模式,也是千珏认为比较容易理解的设计模式。

什么是单例模式

​   惯例先上百度百科上面的介绍。

单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)

​   千珏对单例模式的理解就是通过单例模式创建的类在应用中,同一个时候只能有一个实例

​   我们什么时候会用到单例模式呢,比如你的应用中的配置文件的类,在整个应用中同一个时候只能有一个实例,如果你有多个实例的话,如果我们要改动配置类的时候,就很容易出异常了。(别问千珏为啥知道,说出来都是心酸泪啊)

​   下面,我们就来看一下做成单例的几种方式。

懒汉模式

public class Singleton {
   //创建一个静态实例
    private static Singleton singleton;
   //限制随意创建单例模式
    private Singleton(){}
    //给出一个公共的静态方法返回一个单例
    public static Singleton getInstance(){
      //只有实例为空的时候才创建实例
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

​   上面这种写法是不考虑并发情况下标准的单例模式,一放到有并发请求的程序中,上面的程序就会出现多个实例,这就不是单例模式了。

​   给个并发的例子给你们测试一下,以防止你们有些人要问千珏,为什么在并发请求中就不是单例模式了呢,千珏是不是你在骗我们。千珏是个好人不会骗人的[狗头]

​   为什么一到并发的时候就会创建多个实例呢,因为并发访问的时候一个线程还未创造出实例之前另外一个线程就进来了这个时候实例还没创建好呢,又判断了一次,这个时候因为 前一个线程还没创建好,所以这个线程也创建了一个实例,所以 就造成了这个单例模式中会出现多个实例的原因。

​   为了解决这种情况,我们想到最简单的应该是下面这种形式。

public class SynchroizedSingleton {
    private static SynchroizedSingleton singleton;
    private SynchroizedSingleton(){}
    public synchronized static SynchroizedSingleton getInstance(){
        if(singleton == null){
            singleton = new SynchroizedSingleton();
        }
        return singleton;
    }
}

​   上面的代码在并发应用中是可以用了,但是这个效率太慢了,在一个线程访问这个方法时,其它所有的线程都在等待状态中。

​   我们可以优化下上面的类我们只要在单例的实例还未创建的时候,在实例创建以后,获取实例的方法就没有必要进行同步控制了,所以可以将上面的代码优化成以下的样子。

public class SynchroizedSingleton {
    private volatile static SynchroizedSingleton singleton;
    private SynchroizedSingleton(){}
    public static SynchroizedSingleton getInstance(){
        if(singleton == null){
            synchronized(SynchroizedSingleton.class){
              if(singleton == null){
                singleton = new SynchroizedSingleton();
              }
            }
        }
        return singleton;
    }
}

​   上面的代码就是很多书上面标准的双重加锁,为什么同步块里面还要判断一下实例是不是空的呢,因为一个线程如果创建好了实例准备返回的时候,这个时候另外一个线程进来了,如果同步块里面没有判断那么又会创建了多个实例了,为什么要加 volatile呢?因为应该在你创建好实例的时候就是要把这个实例立即写入主存,如果没写的话,同步块里面的判断也失去了作用。

如果不知道volatile关键词是什么意思的可以留言给我,如果人多的话,我会写一篇介绍volatile的文章,记得关注我哦。

饿汉模式

​   饿汉式单例

public class Singleton {
    private static Singleton singleton = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return singleton;
    }
}

​   上述的代码就是最简单的单例模式,但是千珏不建议你们使用这种方式,这种方式的缺点就是如果我一旦使用了这个类别的static方法但是我们不准备使用这个实例,这样就会让内存浪费。

​   还有一种方式就是使用内部类的方式

public class Singleton {
    private Singleton(){}
    public static Singleton getInstance(){
        return SingletonInstance.singleton;
    }
    private static class SingletonInstance{
        static Singleton singleton = new Singleton();
    }
}

​   上面就是使用静态的内部类作为单例,为什么上面的单例也可以呢,因为static变量只初始化一次,所以 singleton还是单例的。

总结

​   上面就是单例模式基本上的写法了,如有遗漏还请各位大佬在下面补充一下,千珏在这里先行谢过了。
​   这次介绍的单例模式写的或许还有些问题,麻烦大佬们不要喷千珏,毕竟千珏还是java学习路上的新手。
  感谢各位观看。
​  下期介绍设计模式中的工厂模式,如果有兴趣的麻烦大家动动手点个赞和关注吧,也欢迎大家来微信里找我玩,微信搜索千珏(jue).

在这里插入图片描述
在这里插入图片描述