1、模式简介

  单例模式在代码中是非常常用的,如线程池、数据库连接、注册表、共享资源、一些非常消耗资源的组件,等等。

单例模式主要解决如下问题:

  • 确保一个特殊类的实例是独一无二的;
  • 确保这个类的实例非常容易访问(提供了这个类的一个全局访问指针);

以下情况下可以使用单例模式:

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时;
  • 当要创建的对象非常耗费资源,而且全局用到了很多次时;
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

2、实现方法

2.1、整体思路

单例模式的整体思路如下:

  • 在单例类中创建一个私有的本类对象;
  • 将单例类的构造方法私有化,即用private修饰;
  • 创建一个静态的方法getInstance(),返回本类类型的变量,在这个方法中返回单例。

  根据这种思路,单例模式分为饿汉式单例和懒汉式单例。

2.2、饿汉式

  饿汉式单例即在声明实例对象的时候就直接创建对象,在需要引用的时候直接引用。

  饿汉式单例的简单代码如下:

/**
* 饿汉式单例的单例类
*/
public class HungerSingleton {
// 在声明单例变量的时候直接初始化
private static HungerSingleton instance = new HungerSingleton(); // 私有化构造方法,避免外界直接访问
private HungerSingleton() {
System.out.println("我是饿汉式单例的唯一实例!");
} // 公共的静态方法,用于外界调用该方法来创建唯一的对象
public static HungerSingleton getInstance() {
return instance;
} public void introduce() {
System.out.println("我是introduce()方法,我被调用了!" + Thread.currentThread().getName());
}
}

  测试代码:

public class Test {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start();
}
}

  运行结果:

  从代码和运行结果来看,我们在五个线程中分别调用了HungerSingleton.getInstance()方法来创建对象,但HungerSingleton类的构造方法只被调用了一次,也就是说,五次调用只创建了一个对象。可以看出,饿汉式单例不仅保证了只创建一个对象,也保证了线程安全。

  综上,饿汉式单例的优点是线程安全的,即无论在多少个线程中调用这个对象,调用的都是事先声明好的;缺点是声明出来的对象可能很占用系统资源,会在调用之前影响系统的工作状态。

2.3、懒汉式

  懒汉式单例即在声明的时候不创建对象,在需要引用的时候先创建对象,然后引用。

  懒汉式单例的简单代码如下:

public class LazySingleton {
// 声明变量,但不初始化
private static LazySingleton instance; // 私有化构造方法,避免外界直接访问
private LazySingleton() {
System.out.println("我是懒汉式单例类的对象,我是唯一的!");
} // 公共的静态方法,用于外界调用该方法来创建唯一的对象
public static LazySingleton getInstance() {
// 在调用对象的时候判断对象是否存在,如果存在则直接使用,否则才创建对象
if (instance == null) {
instance = new LazySingleton();
}
return instance;
} public void introduce() {
System.out.println("我是introduce()方法,我被调用了!" + Thread.currentThread().getName());
}
}

  测试代码和饿汉式单例的测试代码基本相同,也是五个线程同时访问。运行结果如下:

  从代码和运行结果来看,当有多个线程同时访问这个单例对象的时候,就有可能创建出多个对象,这不符合单例模式的初衷。为了解决这个方法,我们需要对懒汉式单例进行一定的改进,方法是为创建单例的代码加一层双重检查锁。具体代码如下:

      // 公共的静态方法,用于外界调用该方法来创建唯一的对象
public static LazySingleton getInstance() {
// 在调用对象的时候判断对象是否存在,如果存在则直接使用,否则才创建对象
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}

  再次运行,运行结果如下:

  综上,懒汉式单例的优点是在没有调用单例对象之前不会影响系统的工作状态;缺点是线程不安全,即如果在多个线程中同时访问这个单例对象,很可能会创建出多个对象,但这种缺点是可以改进的,方法就是加双重检查锁。

3、模式优缺点

单例模式有以下优点:

  • 使用单例模式可以严格的控制用户怎样以及如何访问它;
  • 节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式可以提高系统的性能;
  • 允许可变数目的实例。

单例模式有以下缺点:

  • 单例模式的扩展有很大的困难;
  • 单例类职责过重,在一定程度上违背了“单一职责原则”;
  • 滥用单例模式可能产生一些负面问题,如一个对象的访问过多可能引起对象溢出;如果实例化的对象长时间不被使用,系统会默认为是垃圾而回收,导致对象状态的丢失。

4、多实例单例

  前面说过,单例可以控制生成的对象的个数或不同种类的对象。例如,一个单例类可能会有多个子类,这些子类都是对父类的扩展,每个子类都有其独特的作用,那么我们可能就会控制每个子类都可以有一个单例对象,这时就用到了多实例单例。

  其实想想也不奇怪,我们既然能控制一个类只能生成一个对象,那么控制这个类生成固定个数个对象自然也不成问题。要解决这个问题,我们需要用到集合类,Map、List都可以,每当需要创建一个对象的时候,我们就遍历这个集合,判断这个对象在集合中有没有单例,如果有,则直接调用集合中的对象;如果没有,才可以创建一个对象然后存到集合中。

  当然,这种多实例单例的方法也不仅用在每个类只能创建一个实例的情况,也可以用在一个类可以创建多个相同的实例的情况,其解决方法依然是使用集合,具体方法如上,就不多说了。

  最后贴出单例模式的GitHub地址:【GitHub - Singleton】

【设计模式 - 2】之单例模式(Singleton)的更多相关文章

  1. 【白话设计模式四】单例模式(Singleton)

    转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...

  2. IOS设计模式浅析之单例模式(Singleton)

    说在前面 进入正式的设计模式交流之前,扯点闲话.我们在项目开发的过程中,经常会不经意的使用一些常见的设计模式,如单例模式.工厂方法模式.观察者模式等,以前做.NET开发的时候,认真拜读了一下程杰老师的 ...

  3. 设计模式系列之单例模式(Singleton Pattern)——确保对象的唯一性

    模式概述 模式定义 模式结构图 饿汉式单例与懒汉式单例 饿汉式单例 懒汉式单例 模式应用 模式在JDK中的应用 模式在开源项目中的应用 模式总结 主要优点 适用场景 说明:设计模式系列文章是读刘伟所著 ...

  4. Net设计模式实例之单例模式( Singleton Pattern)

    一.单例模式简介(Brief Introduction) 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯 ...

  5. JavaScript设计模式 Item 6 --单例模式Singleton

    单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池.全局缓存.浏览器的window对象.在js开发中,单例模式的 ...

  6. 设计模式系列之单例模式(Singleton Pattern)

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.这种模式涉及到一个单一的类,该类负责创建自己的对象 ...

  7. Python设计模式 - 创建型 - 单例模式(Singleton) - 十种

    对于很多开发人员来说,单例模式算是比较简单常用.也是最早接触的设计模式了,仔细研究起来单例模式似乎又不像看起来那么简单.我们知道单例模式适用于提供全局唯一访问点,频繁需要创建及销毁对象等场合,的确方便 ...

  8. 我的设计模式学习笔记------>单例模式(Singleton)

    一.前言 有些时候,允许自由创建某个类的实例是没有意义,还可能造成系统性能下降(因为创建对象所带来的系统开销问题).例如整个Windows系统只有一个窗口管理器,只有一个回收站等.在Java EE应用 ...

  9. 设计模式之三:单例模式singleton

    单例设计模式确切的说就是一个类只有一个实例,有一个全局的接口来访问这个实例.当第一次载入的时候,它通常使用延时加载的方法创建单一实例. 提示:苹果大量的使用了这种方法.例子:[NSUserDefaul ...

  10. 《JAVA设计模式》之单例模式(Singleton)

    在阎宏博士的<JAVA与模式>一书中开头是这样描述单例模式的: 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 单例模式的 ...

随机推荐

  1. PHP 单一入口

    单一入口概述 单一入口的应用程序就是说用一个文件处理所有的HTTP请求,例如不管是列表页还是文章页,都是从浏览器访问index.php文件,这个文件就是这个应用程序的单一入口. 打个比方,大家都要上W ...

  2. 读书笔记之 - javascript 设计模式 - 观察者模式

    在事件驱动的环境中,比如浏览器这种持续寻求用户关注的环境中,观察者模式是一种管理人与其任务(确切的讲,是对象及其行为和状态之间的关系)之间的关系的得力工具.用javascript的话来讲,这种模式的实 ...

  3. Nginx 独立图片服务器的搭建

    为什么需要独立图片服务器? 如果你留心的话,可以发现,现在主流的网站都是有单独的图片服务器的,例如,人人网的为rrimg,淘宝的为taobaocdn,下面还有很多的二级域名. 独立的图片服务器有诸多好 ...

  4. ubuntu 选择最快得源

    root权限.新版的Ubuntu(12.04)已经不再自带类似apt-spy之类的选择最快的源的命令行工具,默认的源经常那个龟速啊……手动测试哪个源在当前网络环境下会比较快还是比较累的,这里整理一个脚 ...

  5. Maven入门详解以及Eclisp的集成

    1.首先要安装Maven到操作系统上 Maven的下载页面:http://maven.apache.org/download.html Maven跟Tomcat很像,下载下来后直接解压在指定的目录就安 ...

  6. Spring 中设置依赖注入

    package com.ysq.vo; public class User { private int uid; private String uname; private String pwd; p ...

  7. clang: error: invalid deployment target for -stdlib=libc++ (requires iOS 5.0 or later)

    低于5.0版本的不支持设置成 或 在Xcode中做如下配置 静态库工程也要做同样设置

  8. Jinja2学习笔记暨官方文档的翻译

    http://blog.csdn.net/lgg201/article/details/4647471 呵呵, 刚刚看完Python模板引擎Jinja2的文档, 感觉很好, 觉得动态语言真是很好.  ...

  9. VLD(Visual LeakDetector)内存泄露库的使用

    VLD简介 由于C/C++语言没有所谓的垃圾收集器,内存的分配和释放都需要程序员自己来控制,这会给C/C++程序员带来一定的困难.当您的程序越来越复杂时,它的内存管理也会变得越来越困难.内存泄漏.内存 ...

  10. bzoj3083 3306

    又见bzoj的语言歧视,囧……bzoj3083过了本地的数据在上面出现各种奇葩的TLE835083 phile 3083 Time_Limit_Exceed 17092 kb 4872 ms Pasc ...