你真的了解Singeton吗?

原创 2016年09月05日 20:13:26

时代在进步,随着人们对编程语言理解程度的不断加深,设计模式也在不断进步。笔者对Singleton模式的理解,也经历了不断加深的一个过程。今天我们就来聊聊Java语言最常用的设计模式之一——单例模式(Singleton)。

1. 什么是Singleton

Singleton的正式提出,自然来自GoF的《设计模式》。单例模式中的“单例”通常用来代表那些本质上具有唯一性的系统组件(或者叫做资源)。比如文件系统、资源管理器等等。

GoF:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

对于Java语言,需要考虑的问题就更复杂一点,主要是Singleton定义的范围。不说明范围的Singleton就是在耍流氓

1、对于Java,一般单实例的范围应该时JVM内唯一。

2、每个类加载器(ClassLoader)都能够加载同一个类,使得在同一JVM中存在多个实例。所以在多加载器情形,应当首先考虑单实例的应用范围。

对于Singleton的各种书写方式及其分析,有一篇文章比较全面:http://www.cnblogs.com/520yang/articles/4535096.html

2. 为什么使用Singleton

本段中,如无特殊说明,默认Singleton范围是本JVM,只有一个ClassLoader。

Singleton有一个“强力竞争者”:静态变量+静态变量的类。很多情况下它们能够实现同样的功能,所以我们必须弄明白Singleton独有的优势。

我们先看一下Singleton的基本形式:

public class SingletonBasic {
    // The Singleton factory part
    private static SingletonBasic instance;
    public static SingletonBasic getInstance(){
        if (instance == null){
            instance = new SingletonBasic();
        }
        return instance;
    }
    // The Singleton provided functions
    private Object var1;
    private Object var2;
    private SingletonBasic(){}

    public void doSomething(){System.out.print(var1);}
    public int returnSomething(){
        return 0;
    }
}

通过观察,我们可以从这个基本形式,总结一下Singleton的特点。这种写法虽然在工程代码中不建议,但是作为麻雀解剖却是极好的。

  1. 自己负责自身实例的创建和维护。
  2. 通过实例对外提供功能。
  3. 有延迟加载的效果。
  4. 扩展为N实例模式时,外部不感知。

简单说,Singleton就是工厂+单态。相对于如下的静态方式,单例模式的特点,似乎大部分都有问题了。

public class SingleStatic {
    private static Object var1 = new Object();
    private static Object var2 = new Object();

    private SingleStatic(){
        throw new Exception();
    }

    public static void doSomething(){
        System.out.print(var1);
    }
    public static int returnSomething(){
        return 0;
    }
}

对于上面的写法,

第一,根本就不需要创建实例,而且,不允许外部调用构造函数。这方面比单例模式更加安全(当然单例模式还有更安全的枚举写法)。

第二,所有功能均具备。差别仅仅在于延迟加载,以及可能扩展为N实例的需求。

对于N实例的扩展需求,这本质上是工厂模式的优势所在,算不得单例模式的好处。那么,使用Java单例模式的好处,就只剩下了延迟加载这一个。然而延迟加载是否真的那么重要?以至于要用单独的一个模式来支持它?

面对这个明显的不合理,经过“慎重思考”,我终于又想到一个理由:不需要一直驻留内存,但最多只能有一个实例的情况。

然而,不需要一直驻留内存,则需要使用完后就把内存回收,但是我们的Singleton经典写法,实例却是static的,这意味着只要实例化一次,就永远无法垃圾回收!这是怎么话说的?笔者瞬间凌乱了……

3. 进一步分析

经过上述分析,我们看到,Singleton的应用场景,只有三个:

  1. 需要延迟加载的特性。比如,要加快系统启动速度,用Singleton将服务包装,避免大量数据在类初始化时就加载进来。

  2. 不需要一直常驻内存。这时使用Singleton,由于其实例是static的,所以必须通过显式的方法,在不再需要的时候,将实例注销。

  3. 可能会扩展为对象池。

4. 使用Singleton的正确姿势

A. 多线程

通过上述分析,我们发现,Singleton天生就要应对多线程的场景。所以,线程安全肯定是必须考虑的因素。我们在网上一搜,多数介绍Singleton相关的文章都是把多线程放在首位,而且对线程安全的写法的分析也是最早出现的。现在我们应该明白为什么会如此了。

在所有线程安全的写法中,静态内部类和枚举是被人们所推荐的。尤其是枚举,更是得到了一些大牛的支持。

B. 测试

然而,Singleton模式还有另外一个扰不过去的问题,那就是测试困难。这个难题如何解决呢?

其实单例模式对外提供的是一组服务。之所以测试困难,是因为这些服务依赖于内部的状态,而内部的状态值在初始化时又依赖于其它不方便模拟的资源。下边的代码是一种解决思路:

public enum XXService{
    service;
    private int value;
    XXService(){
        DataProvider provider = Factory.getProvider();
        value = provider.provide();
    }
    public int getValue(){return value;}
}
interface DataProvider{
    int provide();
}
// 注意这里是包权限
class Factory {
    private static instance = new DefautProvider();
    static DataProvider getProvider(){
        return instance;
    }
    static void set(DataProvider userDefine){
        instance = userDefine;
    }
}

C. 需要注销的情况

在这种情况下,单实例往往就不能把工厂与自身结合到一起了。于是工厂独立出来后,形成一个Registry。单实例在初始化时,把自己注册到Registry中;一旦不再需要,便可以注销。

如果硬要使用经典模式,也不是不可以,只是需要增加一个delete函数来消除静态实例。

版权声明:本文为博主原创文章,未经博主允许不得转载。

你真的了解HTML吗?–雅虎面试题

有这么一段HTML,请挑毛病:   哥写的不是HTML,是寂寞。  我说:不要迷恋哥,哥只是一个传说 考点1:html和 xhtml的区别 ...
  • ymjring
  • ymjring
  • 2013年01月04日 14:56
  • 14681

gradle 详解——你真的了解Gradle吗?

前段时间忽然发现自己对于Android studio的Gradle打包并不了解,这篇博客参考网上众多教程,为大家详细介绍Gradle。 Grade简介 We would like to introdu...
  • u013132758
  • u013132758
  • 2016年08月30日 19:48
  • 4495

你真的了解“程序猿”吗?

为什么你说“就差一个码农了”,我们是拒绝的         根本原因是 90% 这么说的人不懂技术,不了解行业,把技术实现想太简单,以为编程就是打字。分开来说包括以下 4 个方面:不尊重程序员、不只缺...
  • luo201227
  • luo201227
  • 2015年05月14日 09:21
  • 2409

Testin第三期-你真的了解Monkey吗(陈晔)

  • 2015年05月06日 18:38
  • 1.9MB
  • 下载

Java技术——你真的了解String类的intern()方法吗

0.引言 什么都先不说,先看下面这个引入的例子: [java] view plain copy String str1 = new String("SEU")+ new Stri...
  • baidu_31657889
  • baidu_31657889
  • 2016年08月25日 16:30
  • 8543

敏捷开发实战(二)--你真的了解Scrum吗?

随着敏捷开发越来越流行,人人都在谈敏捷,人人也都在学习scrum等敏捷开发方法。。。...
  • jiuqiyuliang
  • jiuqiyuliang
  • 2015年06月30日 00:06
  • 15863

常用的console 的方法,你真的了解 console 吗

对于前端开发者来说,在开发过程中需要监控某些表达式或变量的值的时候,用 debugger 会显得过于笨重,取而代之则是会将值输出到控制台上方便调试。最常用的语句就是console.log(expres...
  • kongjiea
  • kongjiea
  • 2015年05月26日 13:04
  • 6694

浅谈 指针-你真的了解指针吗?

指针 关键字:另类,新奇 最近在学习的过程中,产生了疑问,也得到了解决,对于指针的了解又上了一个层面 -----下面,我就对 (平日里以为很了解的指针)来发表一下我的一个另类的观点  ...
  • qq_28727073
  • qq_28727073
  • 2016年08月02日 10:03
  • 957

Android 消息机制——你真的了解Handler?

原文 前言 Android的消息机制主要是指Handler的运行机制,对于大家来说Handler已经是轻车熟路了,可是真的掌握了Handler?本文主要通过几个问题围绕着Handler展开深入并拓...
  • zrbcsdn
  • zrbcsdn
  • 2017年11月18日 11:05
  • 88

你真的了解:IIS连接数、IIS并发连接数、IIS最大并发工作线程数、应用程序池的队列长度、应用程序池的...

IIS连接数 一般购买过虚拟主机的朋友都熟悉购买时,会限制IIS连接数,这边先从普通不懂代码用户角度理解IIS连接数 顾名思义即为IIS服务器可以同时容纳客户请求的最高连接数,准确的说应该...
  • dream_ll
  • dream_ll
  • 2015年04月23日 10:03
  • 10605
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:你真的了解Singeton吗?
举报原因:
原因补充:

(最多只允许输入30个字)