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. ACM Sdut 2158 Hello World!(数学题,排序) (山东省ACM第一届省赛C题)

    题目描述 We know thatIvan gives Saya three problems to solve (Problem F), and this is the firstproblem. ...

  2. iPhone分辨率

    分辨率和像素 1.iPhone5           4"     分辨率320x568,像素640x1136,@2x 2.iPhone6           4.7"  分辨率3 ...

  3. iOS: 学习笔记, 透过Boolean看Swift(译自: https://developer.apple.com/swift/blog/ Aug 5, 2014 Boolean)

    透过Boolean看Swift 一个简单的Bool类型内部就包含了许多Swift主要功能, 如何构建一个简单类型是有趣的演示. 本文将创建一个与Bool类型在设计与实现上非常相似的新MyBool类型. ...

  4. Python: 使用zipfile+io模块在内存中进行zip操作

    #!/usr/bin/env python #coding=utf-8 ''' 版权所有 (c) 2014 yao_yu (http://blog.csdn.net/yao_yu_126) 本代码采用 ...

  5. Swift 类和结构体的简单认识

    类和结构体的共同点: 定义属性用于存储值 定义方法用于提供功能 定义附属脚本用于访问值 通过拓展增加默认实现的功能 定义构造器用于生成初始化值 实现协议以提供某种标准功能 类是引用类型 结构体是值类型 ...

  6. MFC学习指南大纲

    最近一直在做MFC的项目,顺便学习一下以下MFC核心知识吧: 大纲: 1. 消息队列 2. send message 3. post message 4. 面向对象编程 5. 指针 一个一个来学习哈.

  7. 网页错误404 or 500

    HTTP 错误 400 400 请求出错 由于语法格式有误,服务器无法理解此请求.不作修改,客户程序就无法重复此请求. HTTP 错误 401 401.1 未授权:登录失败 此错误表明传输给服务器的证 ...

  8. [BZOJ 3620] 似乎在梦中见过的样子 【KMP】

    题目链接:BZOJ - 3620 题目分析 这道题使用 KMP 做 O(n^2) 的暴力就能过. 首先,我们依次枚举字串左端点 l ,然后从这个左端点开始向后做一次 KMP. 然后我们枚举右端点 r  ...

  9. 【UVA11294】Wedding (2-SAT)

    题意: 有N-1对夫妻参加一个婚宴,所有人都坐在一个长长的餐桌左侧或者右侧,新郎和新娘面做面坐在桌子的两侧.由于新娘的头饰很复杂,她无法看到和她坐在同一侧餐桌的人,只能看到对面餐桌的人.任意一对夫妻不 ...

  10. css的定位机制

    牛腩新闻发不系统中遇到了CSS(Cascading style sheets),第一次接触,比较陌生还!因为CSS很多关于元素定位的问题,并且很多情况下元素的位置以像素精度计.一个不小心就很头疼,为此 ...