设计模式-单例模式

单例模式

第一篇博客,开通博客的目的一个是进行知识的分享,将自己的所学进行整理,并将整理好的结果分享出来;另一方面是养成一个好的习惯,可以通过博客当作自己的一个public记事本。最近在学设计模式相关的知识,就从设计模式开始学习整理把。由于刚步入工作,对设计模式不是特别的了解。在大学的项目中,用的最多的是MVC设计模式,其他的设计模式基本上就没怎么用过,就连普通的接口都用的少。进入工作后,发现很多地方都涉及到设计模式相关的知识点,所以觉得设计模式十分的重要。为了克服懒散,通过博客来进行自己的学习效果,进而督促自己来进行学习。对设计模式的学习是以《Head First 设计模式》为主,通过也参考着菜鸟教程进行学习。废话到此为止,开始整理。

简单描述:独一无二的对象

所属类型:创建型模式

适用情况:某个全局使用的类被频繁的创建和销毁,也就是对于那些我们只需要一个的对象

适用对象:线程池,缓存,对话框,处理偏好设置和注册表的对象,日志对象,打印机,显卡等设备的驱动对象

实现原则:确保一个类只有一个实例,并提供全局访问点。换句话来说就是:单例模式没有公有构造器,也就是无法在该类的外面new出来该类的实例。

实现方法

  1. 利用一个私有静态变量来记录单例类的唯一实例

  2. 将构造器声明为私有,也就是只有在类的内部才能调用构造器,进而对它进行实例化

  3. 通过一个公有的静态方法来进行实例化,并将之前创造出来用来记录唯一实例的对象进行返回

接下来对单例模式的几种实现方法进行整理。

1. 懒汉式,线程不安全

所谓的懒汉式,从中提取出关键词就是“懒”。怎样才能实现“懒”呢?这里的懒其是就是为了实现懒加载。换句话来说就是"如果我不需要这个实例,我就不去给他实例化"

先上代码吧:

复制代码
public class Singleton {
​
    //创建一个私有变量用来保存单例模式的唯一实例
    private static Singleton mInstance;
    
    //将构造声明为私有
    private Singleton() {
    }
​
    //通过公有的静态方法对其进行实例化
    public static Singleton getInstance() {
        if (mInstance == null) {
            mInstance = new Singleton();
        }
        return mInstance;
    }
}
复制代码

就这样,简单的一个单例模式就完成了。如果不考虑线程安全,也就是不需要考虑多线程的情况,那么这样子的单例用起来没问题。如果考虑到多线程的情况,就会存在一些问题。下面再对这种单例模式进行简单总结,然后进行单例模式的其他形式的整理。

是否实现懒加载多线程是否安全实现难易程度

 

2. 懒汉式,线程安全

上面讲到了一个单例模式的简单实现,也提到了那是一种线程不安全的做法。下面就要对上面的情况进行修改,然后让他达到线程安全。

还是先上代码吧:

复制代码
public class Singleton {
​
    //创建一个私有变量用来保存单例模式的唯一实例
    private static Singleton mInstance;
​
    //将构造声明为私有
    private Singleton() {
    }
​
    //通过公有的静态方法对其进行实例化
    public static synchronized Singleton getInstance() {
        if (mInstance == null) {
            mInstance = new Singleton();
        }
        return mInstance;
    }
}
复制代码

和线程不安全的代码相比较,变化只有一处,就是添加了synchronized关键字。添加了synchronized关键字,就可以保证在多线程情况下是线程安全的。但这样同时也带来了一个问题,如果每次使用这个单例对象的时候都要进行一次同步,频繁的进行访问,那将会十分的耗时。所以这种方式适合于对该方法使用不是特别频繁的情况。也对这种情况进行简单小结。

是否实现懒加载多线程是否安全实现难易程度

 

3. 饿汉式

从多线程的角度来进行考虑,懒汉式的缺点是一旦对其使用的频率过高,效率就成了问题,接下来从效率的角度来进行考虑。

还是一样,先上代码:

复制代码
public class Singleton {
    //类加载时进行初始化
    private static Singleton mInstance = new Singleton();
​
    //将构造声明为私有
    private Singleton() {
    }
​
    //调用静态方法时将该对象返回
    public static Singleton getInstance() {
        return mInstance;
    }
}
复制代码

这种方法是比较常用的,和懒汉式的方法相比较而言,饿汉式在类加载时就进行了初始化,也就是我们不管用还是不用,这个对象就已经创建好了,我们随用随取,不需要再判断这个实例是否存在。但相比于懒汉式而言,饿汉式并没有实现懒加载的效果。也就是说在一定程度上饿汉式比较浪费资源。下面也进行一个简单的小结。

是否实现懒加载多线程是否安全实现难易程度

 

4. 双重检查加锁

从名字上来看,我们可能要进行两次什么操作了。先上代码吧:

复制代码
public class Singleton {
    //创建一个私有变量用来保存对象
    private volatile static Singleton mInstance;
​
    //构造方法私有化
    private Singleton() {
    }
​
    //根据条件来对mInstance进行实例化
    public static Singleton getInstance() {
        if (mInstance == null) {
​
            synchronized (Singleton.class) {
                if (mInstance == null) {
                    mInstance = new Singleton();
                }
            }
        }
        return mInstance;
    }
}
复制代码

和懒汉式的代码进行比较,发现多了个volatile关键字,这个关键字在这的作用主要是保持内存可见性,换句话来说就是:在一个线程对mInstance的值进行了修改,在另一个线程就能马上看到。接着对mInstance进行实例化的步骤中,可以看到多了一层判断,这样一方面能够保证线程的安全,另一方面能够保持比较高的性能。双重检查加锁的模式在JDK1.5之后才开始使用,在JDK1.5之前许多JVM对于volatile关键字的实现会导双重检查加锁的失效。最后再对这种单例模式进行简单总结。

是否实现懒加载多线程是否安全实现难易程度
较复杂

5. 小结

最后再来整理一下单例模式的几个要点:

  • 单例模式确保程序中一个类最多只有一个实例
  • 单例模式提供访问这个实例的全局点
  • 在Java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量
  • 确定在性能和资源上的限制,然后小心地选择适当的方案来实现单例,以解决多线程的问题
  • 如果不是使用JDK1.5之后的版本,双重检查加锁实现会失效
  • 如果使用多个类加载器,可能导致单例失效而产生多个实例
  • 如果使用JVM1.2或者是之前的版本,就必须建立单例注册表,以免垃圾回收器将单例回收
posted @   忠肝义胆-多隆  阅读(16)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示