设计模式之单件模式(Singleton Pattern)引出单例模式经典单例模式的实现定义单件模式经典单件模式存在的问题解决单例模式的多线程问题

单件模式,也叫单例模式,可以说是设计模式中最简单的一种。顾名思义,就是创造独一无二的唯一的一个实例化的对象。

为什么要这样做呢?因为有些时候,我们只需要一个对象就够了,太多对象反而会引起不必要的麻烦。比如说,线程池,缓存,打印机,注册表,如果存在多个实例的话,反而会导致许多问题!

引出单例模式

我们通过一个小问题引出单例模式!

  • 如何创建一个对象?我们都知道 new MyObject();
  • 当我们需要创建与另外一个对象时,只需要再次new MyObject();即可
  • 那么如下这样的代码是正确的么?
public MyClass{
  private MyClass() {}  
}
  • 看过去这是合法的定义,没有什么语法错误。但仔细想想,含有私有构造器的话,只能在MyClass内调用构造器。因为必须有Myclass的实例才能调用构造器,但因为没有其他类可以取得它的实例,所以,我们无法实例化它,这像不像鸡生蛋还是蛋生鸡的问题?哈哈哈
  • 为了解决这个问题,取得MyClass类的实例,我们创造一个静态方法
public MyClass{
  private MyClass() {}  
  public static MyClass getInstance() {
    return new MyClass();
  }
}
  • 我们添加了一个静态的类方法,它可以返回一个对象实例,由于他是public,所以外部可以调用他。这实际上就实现了一个简单的单例模式。

经典单例模式的实现

public class Singleton {
    private static Singleton uniqueInstance;
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    } 
}
  • 这里实现了一个概念,叫延迟实例化(lazy instance)。因为在我们不需要实例的时候,这个实例就永远不会被实例化。

定义单件模式

单件模式的定义: 确保一个类只有一个实例,并提供一个全局访问点。

这定义应该很好理解,我们结合类图说明:

Paste_Image.png

经典单件模式存在的问题

经典单件模式实际中存在这一定的问题,在第一次初始化实例的时候,如果同时有不同的线程访问,那么可能最后不只实例化出一个对象。

Paste_Image.png

如图所示,如果两个线程如图所示的顺序交错执行,那么最后会实例化两个对象! 这就是经典单例模式存在的多线程问题。

解决单例模式的多线程问题

synchronize

显然最简单的一种解决方法就是同步getInstance方法。

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

这样显然可以很好的解决问题,但是同步会降低效率。

急切实例化

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

在任何线程访问uniqueInstance变量前,我们保证一定已经创建了这个实例。

双重检查加锁

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏轮子工厂

务实基础篇--Java内存模型及GC原理

堆是Java代码可及的内存,留给开发人员使用的;非堆是JVM留给自己用的,包含方法区、JVM内部处理或优化所需的内存(如 JIT Compiler,Just-i...

18620
来自专栏小工匠技术圈

【Java小工匠】JavaNIO-缓存区基础

  缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对输入/输出(I/O)的数据作临时存储,这部分预留的内存空间就叫做缓冲区。

13020
来自专栏轮子工厂

蚂蚁金服Java研发工程师的春招面试经历 | 双非大佬教你如何成为offer收割机

首先,我的面试经历和一下面霸、收割机大佬相比,不是特别丰富,只是略有感悟,分享这几个月来的心路历程,也让后来者可以借鉴一下而已。

15720
来自专栏后端技术探索

京东老司机:巧用Nginx+Lua解决数据托底大痛点

随着京东商城的发展,内部也出现了一些比较有意思的小系统小模块来解决一些业务系统的痛点,而这些小系统小模块虽说不复杂但是解决了当时的痛点。数据托底就是其中一个痛点...

27010
来自专栏Keegan小钢

我的个人品牌运营之路

今早打开我的微信公众号,突然看到用户数飙升了。接着再看看博客网站的记录,同样也出现了飙升。这是截止到昨天统计的数据:

10630
来自专栏行者常至

(Java)请求方式之GET、POST 浅析

·不同的请求方式不仅仅在数据传输时会有所不同,在表单提交及服务器端处理时也会采用不同的方式。而区分不同种类的请求方式也会使得浏览器采用不同的缓存方式处理后续请求...

16120
来自专栏木子昭的博客

免费将qq音乐歌曲导入网易云音乐

早上打开朋友圈,发现了"90后", "周杰伦", "稻香"等词, 于是打开网易云, 想刷一下周杰伦的歌, 但发现网易云内周杰伦的歌早下架了, 但我就是想用网易云...

27320
来自专栏后端技术探索

面向切面缓存设计

在互联网行业,缓存作为一种家喻户晓的技术,在各个系统中起着提效降压的作用。但是缓存的引入,也使得处理逻辑变得复杂,尤其在当下微服务大行其道,一个大型系统动辄十几...

15620
来自专栏IT派

被高通裁员两次,清华毕业华裔工程师跳楼身亡!中年IT男,为何这么难?

美国当地时间6月17日晚,位于美国圣地亚哥的高通公司(Qualcomm)总部发生员工跳楼事件,死者华裔工程师大卫·吴(David Wu)从总部办公楼六楼跳下,当...

9010
来自专栏轮子工厂

在浏览器中输入网址到页面显示出来,这中间到底发生了什么?

(1)检查本地hosts文件是否有这个网址的映射,如果有,就调用这个IP地址映射,解析完成。

10830

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励