`

设计模式——单例模式

阅读更多

前面已经对工厂方法模式、抽象工厂模式、建造者模式、原型模式进行了介绍,今天要介绍的是设计模式的创建型模式的最后一个模式——单例模式。

 

一、单例模式动机

顾名思义,就是某个类只有一个实例,这种场景其实在软件开发中屡见不鲜,因为对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。

 

那么如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。

 

一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。

 

单例模式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。

 

由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。

 

允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。

 

二、单例模式定义

单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

 

单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。

 

三、单例模式结构

单例模式包含一个角色:

Singleton:单例

 

但是单例模式有两种实现形式:懒汉式单例类和饿汉式单例类,具体如下:



 饿汉式单例类

 


懒汉式单例类

 

可以看到饿汉式和懒汉式两种方式在初始化的时候有如下不同:

1.饿汉式单例类在自己被加载时就将自己实例化。单从资源利用效率角度来讲,这个比懒汉式单例类稍差些。从速度和反应时间角度来讲,则比懒汉式单例类稍好些。

2.懒汉式单例类在实例化时,必须处理好在多个线程同时首次引用此类时的访问限制问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费大量时间,这意味着出现多线程同时首次引用此类的机率变得较大,需要通过同步化机制进行控制。

 

不管饿汉式还是懒汉式,两种单例模式的实现形式都有一个共同特点:

在单例类的内部实现只生成一个实例,同时它提供一个静态的工厂方法,让客户可以使用它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有。 

 

四、模式分析

单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式包含的角色只有一个,就是单例类——Singleton。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。 

 

在单例模式的实现过程中,需要注意如下三点:

1.单例类的构造函数为私有;

2.提供一个自身的静态私有成员变量; 

3.提供一个公有的静态工厂方法。

 

五、实例分析

还是老规矩,我们拿一个例子进行展示。如下:在操作系统中,打印池(Print Spooler)是一个用于管理打印任务的应用程序,通过打印池用户可以删除、中止或者改变打印任务的优先级,在一个系统中只允许运行一个打印池对象,如果重复创建打印池则抛出异常。现使用单例模式来模拟实现打印池的设计。

 

1.懒汉式单例模式

 

public class PrintSpoolLazy {

	private static PrintSpoolLazy instance;
	
	/**
	 * 构造方法私有化
	 */
	private PrintSpoolLazy(){
		// 初始化打印池数据
		System.out.println("创建了一个打印池对象,对象地址为:" + this);
	}
	
	public static PrintSpoolLazy getInstance(){
		if(instance == null){
			instance = new PrintSpoolLazy();
		}
		return instance;
	}
	
	public void manageJobs() {
		System.out.println("打印池对象地址:" + this + "\n" + "进行了打印作业管理");
	}
	
}
  

 

2.饿汉式单例模式

 

public class PrintSpoolHungry {

private static PrintSpoolHungry instance = new PrintSpoolHungry();
	
	/**
	 * 构造方法私有化
	 */
	private PrintSpoolHungry(){
		// 初始化打印池数据
		System.out.println("创建了一个打印池对象,对象地址为:" + this);
	}
	
	public static PrintSpoolHungry getInstance(){
		return instance;
	}
	
	public void manageJobs() {
		System.out.println("打印池对象地址:" + this + "\n" + "进行了打印作业管理");
	}
	
}

 

 

3.测试类

public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("懒汉式单例模式");
		PrintSpoolLazy printSpoolLazy1 = PrintSpoolLazy.getInstance();
		printSpoolLazy1.manageJobs();
		PrintSpoolLazy printSpoolLazy2 = PrintSpoolLazy.getInstance();
		printSpoolLazy2.manageJobs();
		System.out.println("饿汉式单例模式");
		PrintSpoolHungry printSpoolHungry1 = PrintSpoolHungry.getInstance();
		printSpoolHungry1.manageJobs();
		PrintSpoolHungry printSpoolHungry2 = PrintSpoolHungry.getInstance();
		printSpoolHungry2.manageJobs();
	}

 

运行结果:


  

六、多线程编程中使用单例模式的优化

Java支持多线程编程,每个Java应用程序会启动一个主线程—将main()放在它自己执行空间的最开始处。Java虚拟机会负责主线程的启动(以及比如垃圾收集所需的系统用线程)。程序员负责启动自己的线程。下面来看一个例子,在多线程环境下使用前面懒汉式单例模式。如下图所示:



那么这个时候我们很自然的想到了,采用synchronized关键字对getInstance()方法进行同步,灾难几乎就可以轻易解决了,但是却给性能带来显著的下降,原因如下:

(1) 进入同步化的方法的程序,需要查询哪个线程有权访问对象的方法(即对象上了锁,有钥匙才能开);

(2)同步化会强制线程排队等着执行方法;

(3)同步可能会导致死锁。

实际上,我们只有第一次执行此方法时,才真正需要同步。即一旦设置好了uniqueInstance变量,就不再需要同步化这个方法了。之后每次调用这个方法,同步都是一种累赘。所以采用一种“双重加锁检查”,如下代码:

 

public class PrintSpoolLazy {

	private volatile static PrintSpoolLazy instance;
	
	/**
	 * 构造方法私有化
	 */
	private PrintSpoolLazy(){
		// 初始化打印池数据
		System.out.println("创建了一个打印池对象,对象地址为:" + this);
	}
	
	public static PrintSpoolLazy getInstance(){
		if(instance == null){
			synchronized (PrintSpoolLazy.class) {
				if(instance == null){
					instance = new PrintSpoolLazy();
				}
			}	
		}
		return instance;
	}
	
	public void manageJobs() {
		System.out.println("打印池对象地址:" + this + "\n" + "进行了打印作业管理");
	}
	
}
对静态变量instance加上volatile关键字是确保多线程正确处理该变量,保证线程的可见性。可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的。

 

在getInstance()方法内部,通过双重锁,保证了第一次使用的时候,并且单例变量为空的时候进行了第一次同步,如果同步后发现不为空了就会直接返回静态变量instance,如果为空则会创建一个唯一实例,这样可以大大降低程序性能。

 

谢谢您的关注和阅读,文章不当之处还请您不吝赐教~~~微笑微笑微笑

 

 

 

 

  • 大小: 26.8 KB
  • 大小: 24.8 KB
  • 大小: 28.4 KB
  • 大小: 84.8 KB
分享到:
评论

相关推荐

    设计模式——单例模式(懒汉模式)

    单例模式是软件设计模式中的一种经典模式,它保证了类在任何情况下都只有一个实例存在。这个模式在很多场景下非常有用,例如控制全局资源、管理配置信息等。本文将详细探讨单例模式的懒汉式实现,并结合源码进行解析...

    设计模式——单例

    设计模式是软件工程中的一种最佳实践,用于解决在不同场景下重复出现的问题。...通过阅读提供的"iOS 设计模式——单例"相关资料,可以深入理解在iOS开发环境中如何有效利用单例模式来优化代码结构和提高程序性能。

    C++设计模式——单例模式-附件资源

    C++设计模式——单例模式-附件资源

    设计模式——单例实现

    单例的5中实现及反射和反序列化破解单例。

    java设计模式——单例模式

    【Java设计模式——单例模式】 单例模式是一种常见的软件设计模式,它的核心思想是确保在应用程序的整个生命周期中,某个类只有一个实例存在。这种模式主要用于控制类的实例化过程,减少系统资源的消耗,提高系统...

    设计模式实现——单例模式

    单例模式是软件设计模式中的一种,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下都非常有用,比如控制共享资源、管理系统级别的服务或者简化对象间的交互。单例模式的核心在于...

    C#设计模式(1)——单例模式.pdf

    本次将聚焦于一种较为简单的模式——单例模式。 #### 单例模式概述 单例模式是一种创建型模式,它的核心在于确保某个类只有一个实例,并提供一个全局可访问的访问点。这种模式非常实用,尤其是在需要频繁地创建和...

    java设计模式之——单例模式

    单例模式是软件设计模式中的一种经典模式,其主要目的是控制类的实例化过程,确保在任何情况下,一个类只有一个实例存在。这种模式通常用于管理共享资源或者全局配置,例如数据库连接池、线程池、日志服务等。在Java...

    C++中的三大设计模式:单例模式、工厂模式与观察者模式的代码实现

    内容概要:本文档介绍了三个经典的软件设计模式——单例模式(Singleton Pattern)、工厂模式(Factory Pattern)以及观察者模式(Observer Pattern)的具体实现,并给出了带有详细注释的C++代码范例。对每个设计模式都有...

    设计模式之单例模式Java实现和类设计图

    本次我们将深入探讨两种设计模式——单例模式和装饰模式,它们在Java编程中都有着广泛的应用。 首先,让我们来理解“单例模式”。单例模式是一种创建型设计模式,其核心思想是保证一个类只有一个实例,并提供一个...

    设计模式-单例模式

    **设计模式——单例模式** 单例模式是一种广泛应用于软件设计中的创建型设计模式,它的核心思想是确保一个类只有一个实例,并提供一个全局访问点。这样做的好处在于控制共享资源的访问,比如线程安全的数据库连接池...

    设计模式之PHP项目应用——单例模式设计Memcache和Redis操作类源码

    1)程序功能:单例模式设计Memcache和Redis操作类,采用PHP编写。 2)程序详解地址:http://blog.csdn.net/clevercode/article/details/46410055。 3)原创作品,出自"CleverCode的博客",分类为《设计模式之PHP项目...

    设计模式——策略模式 & 单例模式

    策略模式和单例模式是软件设计中两种非常重要的设计模式,它们在实际开发中有着广泛的应用。在这篇文章中,我们将深入探讨这两种模式的核心概念、实现方式以及如何在实际项目中运用。 策略模式是一种行为设计模式,...

    java设计模式-单例.pdf

    ### Java设计模式——单例模式详解 #### 一、单例模式概述 单例模式是设计模式中的一个重要组成部分,属于创建型模式之一。其主要作用是确保某个类仅有一个实例存在,并提供一个全局访问该实例的方法。这在很多场景...

    IOS应用源码——单例模式.zip

    单例模式是软件设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。在iOS应用开发中,单例模式被广泛用于管理共享资源、实现全局设置、提供网络请求管理器等场景。让我们深入探讨一下单例模式在iOS...

    android开发设计模式之——单例模式详解

    单例模式是设计模式中最常见也最简单的一种设计模式,保证了在程序中只有一个实例存在并且能全局的访问到。比如在Android实际APP 开发中用到的 账号信息对象管理, 数据库对象(SQLiteOpenHelper)等都会用到单例...

Global site tag (gtag.js) - Google Analytics