设计模式二–单例模式(八种具体实现)

文章目录

  • 单例模式
    • 介绍
    • 单例模式实现
      • 饿汉式
        • 静态变量实现
        • 静态代码块实现
      • 懒汉式
        • 线程不安全
        • 同步方法
        • 同步代码块
      • 双重检查
      • 静态内部类实现
      • 枚举实现
    • 说明

单例模式

介绍

单例模式,是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例
并且该类只提供一个取得其对象的静态方法。

单例模式实现

饿汉式

  1. 优点:
    • 写法简单,在类装载时完成实例化。避免线程同步问题
  2. 缺点:
    • 没有懒加载效果。当实例一直未使用时,会造成内存浪费
  3. 下面的两种方法都是使用到了类加载(class loader)的机制,避免了多线程的同步问题。由于导致类加载的原因有多种,
    无法确定是否有其他方式导致了类加载,此时如果直接初始化instance,就无法达到懒加载的目的。通俗点说就是没使用getInstance()
    方法,但是也使用了类,就会导致该类已经加载了,但是并未在此时获取instance实例。
  4. 结论:饿汉式的两种(如下),可以使用,但是不推荐,因为可能造成内存浪费。

静态变量实现

静态代码块实现

懒汉式

线程不安全

其实,我个人说到单例模式的第一感觉就是使用这个线程不安全的方式去实现,毕竟简单嘛。但是,这种方式有一个严重的问题就是
线程不安全,当线程A进入到if语句内,还未执行new语句的时候,另外一个线程B判断此时的instance为null,也进入了if语句。
此时就会产生多个实例。

  1. 结论:虽然达到了懒加载的效果,但是只能单线程使用(开发中一般都是多线程的,不能埋下隐患),所以不推荐使用。

同步方法

在上一个方法的基础上,在实例获取getInstance()方法中添加synchronized关键字,同步方法。

  1. 解决了线程安全问题
  2. 效率过低(因此不推荐使用):由于获取该类实例时都会进行同步,但是实例化却只需要一次,因此,
    后续获取实例都会因为同步导致时间浪费,效率低。

同步代码块

同上一个

双重检查

在看到这个方法的时候,由于看漏了volatile关键字,内心便产生了如下疑惑:好像也会出现不安全的问题br> 这里先解释下Java的volatile关键字:

当 volatile 用于一个作用域时,Java保证如下:(适用于Java所有版本)读和写一个 volatile 变量有全局的排序。
也就是说每个线程访问一个 volatile 作用域时会在继续执行之前读取它的当前值,而不是(可能)使用一个缓存的值。
(但是并不保证经常读写 volatile 作用域时读和写的相对顺序,也就是说通常这并不是有用的线程构建)。
(适用于Java5及其之后的版本) volatile 的读和写创建了一个happens-before关系,类似于申请和释放一个互斥锁。
使用volatile会比使用锁更快,但是在一些情况下它不能工作。
volatile使用范围在Java5中得到了扩展,特别是双重检查锁定现在能够正确工作。

这里,我们需要知道volatile比使用锁更快(效率高)并且保证写instance总在读instance之前(线程安全)即可。

优缺点:

  1. 线程安全(进行类两次if检查且instance用volatile声明)
  2. 第一次实例化后,后续访问if(instance ==null)时直接返回实例化对象,避免反复进行方法同步。
  3. 兼具效率、安全、懒加载(推荐使用)

静态内部类实现

同样,先解释下静态内部类作用:

由于静态内部类是不暴露在外面的,所以使用静态内部类时,我们唯一提供静态内部类类加载的
地方也就是getInstance()入口了,并且静态内部类中的INSTANCE使用了final static进行修饰,
只会在第一次类加载时初始化,因此保证了线程安全。

总结:

  1. 巧妙使用了jvm中类加载以及静态变量的特性,兼具线程安全与懒加载、效率高
  2. 推荐使用

枚举实现

总结:

  1. 可以避免线程同步、防止反序列化重新创建新的对象
  2. 推荐使用

说明

  1. 单例模式保证了系统内存中只存在此类的一个对象,可以节省系统资源,
    在需要频繁创建销毁对象时,可以使用单例模式提高性能。
  2. 需要实例化单例类的时候,需要使用相应的如getInstance()方法获取对象,
    而不应该使用new来实例化对象。
  3. 单例模式运用在需要频繁创建与销毁对象或者是在对象耗时多和资源消耗大时,又
    需要频繁使用类对象、工具类对象、数据库或者文件访问对象的场景(常见的有数据源)。
  4. 这篇文章更多是为了加深了解而写的,如果有错误或者表述不清之处可以评论提醒哦,后续了解了更多jvm底层会进行更新的。

文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树首页概览92440 人正在系统学习中

声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2020年6月1日
下一篇 2020年6月1日

相关推荐