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. 24种设计模式--命令模式【Command Pattern】

    今天讲命令模式,这个模式从名字上看就很简单,命令嘛,老大发命令,小兵执行就是了,确实是这个意思,但是更深化了,用模式来描述真实世界的命令情况.正在看这本书的你,我猜测分为两类:已经工作的和没有工作的, ...

  2. Qt事件循环与状态机事件循环的思考

    写下这个给自己备忘,关于事件循环以及多线程方面的东西我还需要多多学习.首先我们都知道程序有一个主线程,在GUI程序中这个主线程也叫GUI线程,图形和绘图相关的函数都是由主线程来提供.主线程有个事件循环 ...

  3. centos下配置多个tomcat同时运行

    首先安装好jdk,下载好tomcat,我的是apache-tomcat-7.0.50,不用专门配置CATALINA_2_BASE,CATALINA_2_HOME等环境变量. 把tomcat解压到lin ...

  4. 在 Sublime Text 3 中运行 PHP

    参考http://segmentfault.com/blog/tony/1190000000395951 把php添加到环境变量 1.我的电脑->属性->高级系统设置->高级-> ...

  5. frameset标签代码实现网站跳转

    js代码1: document.writeln("<frameset rows=\"0, *\">"); document.writeln(&quo ...

  6. mysql备份sql,脚本

    MySQL 安装位置:/usr/local/mysq 论坛数据库名称为:bbs MySQL root 密码:123456 数据库备份目的地:/var/db_backup/ #! /bin/bash / ...

  7. "The connection for the USB device '###' was unsuccessful. The device is currently in use"

    一.问题描述 1.情景描述 笔者的物理主机系统是“windows7 64位”,想使用“摄像头录像大师”.这个软件在录制视频的过程中,需要调用windows自带的"windows media ...

  8. C# mvc 验证码2

    public class ValidateCode     {         /// <summary>         /// 產生圖形驗證碼.         /// </su ...

  9. OTG线与普通USB线的区别

    转自OTG线与普通USB线的区别 USB数据线是我们常见的设备,OTG线作为近年来随着手机行业的快速发展,逐步进入了我们的日常使用范围.OTG线与普通USB线的有什么区别?       USB数据线用 ...

  10. [spring-framework]Spring定时器的配置和使用

    开发中我们常常会做一些定时任务,这些任务有开始时间,并会按一定的周期或规则执行.如此我们在Java程序开发中使用定时器来处理定时任务. <!-- MessageRequestTask类中包含了m ...