`
java-lazypig
  • 浏览: 6812 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
阅读更多

本人学习java单例模式(Singleton pattern)做的笔记,拿出来分享给像我这样的菜鸟们。希望对菜鸟们有一点点的帮助。

本人参考了:Balan的文章 Java Singleton 实用教程(附源码)

原文地址:http://balan.iteye.com/blog/164873

一、定义

单例模式(Singleton pattern):确保一个类只有一个实例,并提供一个全局的访问点。

这个定义包含两层意思:

第一:我们把某个类设计成自己管理的一个单独实例,同时也要避免其他类再自行产生实例。要想取得单个实例,通过单例类是唯一的途径。

第二:我们必需提供对这个实例的全局访问点:当你需要实例时,向类查询,它会给你返回单个实例。

注意:单例模式确保一个类只有一个实例,是指在特定系统范围内只能有一个实例。有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的。

1。某个框架容器内:如Spring IOC容器,可以通过配置保证实例在容器内的唯一性。

2。再如单一JVM中、单一类加载器加载类的情况可以保证实例的唯一性。

如果在两个类加载器或JVM中,可能他们有机会各自创建自己的单个实例,因为每个类加载器都定义了一个命名空间,如果有两个以上的类加载器,不同的类加载器可能会加载同一个类,从整个程序来看,同一个类会被加载多次。如果这样的事情发生在单例上,就会产后多个Singleton并存的怪异现象。所以如果你的程序有多个类加载,同时你又使用了单例模式,请一定要小心。有一个解决办法是,自行给单例类指定类加载器(指定同一个类加载器)。

二、用处

有一些对象其实我们完全只需要一个即可,如:线程池(threadpool)、缓存(cache)、注册表(registry)的对象、设备的驱动程序的对象等等。事实上,这些类的对象只能有一个实例,如果制造出多个实例,就会导致许多问题的产生,例如:程序的行为异常、资源的过量使用、产生不一致的结果等等。Java Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。

三、Java Singleton模式常见的几种形式

一)。使用立即创建实例,而不用延迟实例化的做法

1。使用全局变量

Java代码 复制代码
  1. //Singleton with final field   
  2. public class Singleton {   
  3.     public static final Singleton uniqueInstance = new Singleton();   
  4.     private Singleton(){   
  5.     }   
  6.     //...Remainder omitted   
  7. }  
//Singleton with final field
public class Singleton {
	public static final Singleton uniqueInstance = new Singleton();
	private Singleton(){
	}
	//...Remainder omitted
}

 在这种方法中,公有静态成员是一个final域(保证了总是包含相同的对象引用)。私有构造函数仅被调用一次,用来实例化公有的静态final域 Singleton.uniqueInstace。由于缺少公有的或者受保护的构造函数,所有保证了Singleton的全局唯一性:一旦 Singleton类被实例化之后,只有一个Singleton实例存在——不多也不少。使用此Singleton类的程序员的任何行为都不能改变这一点。

2。使用公有的静态工厂方法

Java代码 复制代码
  1. //Singleton with static factory   
  2. public class Singleton {   
  3.     private static Singleton uniqueInstance = new Singleton();   
  4.     private Singleton(){   
  5.     }   
  6.     public static Singleton getInSingleton(){   
  7.         return uniqueInstance;   
  8.     }   
  9.     //...Remainder omitted   
  10. }  
//Singleton with static factory
public class Singleton {
	private static Singleton uniqueInstance = new Singleton();
	private Singleton(){
	}
	public static Singleton getInSingleton(){
		return uniqueInstance;
	}
	//...Remainder omitted
}

 第二种方法提供了一个公有的静态工厂方法,而不是公有的静态final域。利用这个做法,我们依赖JVM在加载这个类时马上创建此类唯一的一个实例。JVM保证任何线程访问uniqueInstance静态变量之前,一定先创建此实例。

二)。使用延迟实例化的做法(使用公有的静态工厂方法)

1。非线程安全的

Java代码 复制代码
  1. public class Singleton {   
  2.     private static Singleton uniqueInstance ;   
  3.     private Singleton(){   
  4.     }   
  5.     public static Singleton getInSingleton(){   
  6.         if(uniqueInstance == null){   
  7.             uniqueInstance = new Singleton();   
  8.         }   
  9.         return uniqueInstance;   
  10.     }   
  11.     //...Remainder omitted   
  12. }  
public class Singleton {
	private static Singleton uniqueInstance ;
	private Singleton(){
	}
	public static Singleton getInSingleton(){
		if(uniqueInstance == null){
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}
	//...Remainder omitted
}

 先利用一个静态变量uniqueInstance来记录Singleton类的唯一实例,当我们要使用它的实例时,如果它不存在,就利用私有的构造器产生一个Singleton类的实例并把它赋值到uniqueInstance静态变量中。而如果我们不需要使用这个实例,它就永远不会产生。这就是"延迟实例化(lazy instantiaze)"。但上面这段程序在多线程环境中是不能保证单个实例的。分析如下:

时间点 线程1 线程2 uniqueInstance的值
1 线程1,2同时访问Singleton.getInstance()方法    
2 进入Singleton.getInstance()方法    null
3   进入Singleton.getInstance()方法  null
4 执行if(uniqueInstance == null)判断    null
5   执行if(uniqueInstance == null)判断  null
6 执行uniqueInstance = new Singleton()   Singleton1
7   执行uniqueInstance = new Singleton() Singleton2
8 执行return uniqueInstance;   Singleton1
9   执行return uniqueInstance; Singleton2

如上分析所示,它已产生了两个Singleton实例。

2。多线程安全的

Java代码 复制代码
  1. public class Singleton {   
  2.     private static Singleton uniqueInstance ;   
  3.     private Singleton(){   
  4.     }   
  5.     public synchronized static Singleton getInSingleton(){   
  6.         if(uniqueInstance == null){   
  7.             uniqueInstance = new Singleton();   
  8.         }   
  9.         return uniqueInstance;   
  10.     }   
  11.     //...Remainder omitted   
  12. }  
public class Singleton {
	private static Singleton uniqueInstance ;
	private Singleton(){
	}
	public synchronized static Singleton getInSingleton(){
		if(uniqueInstance == null){
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}
	//...Remainder omitted
}

 通过给getInstance()方法增加synchronized关键字,也就是给getInstance()方法线程加锁,迫使每次只能有一个线程在进入这个方法,这样就可以解决上面多线程产生的灾难了。但加锁的同步方法可能造成程序执行效率大幅度下降,如果你的程序对性能的要求很高,同时你的getInstance()方法调用的很频繁,这时可能这种设计也不符合程序要求了。其实这种加锁同步的方法用在这确实有一定的问题存在,因为对 Singleton类来说,只有在第一次执行getInstance()方法时,才真正的需要对方法进行加锁同步,因为一旦第一次设置好 uniqueInstance变量后,就不再需要同步这个方法了。之后每次调用这个方法,同步反而成了一种累赘。

3。 用"双重检查加锁",在getInstance()方法中减少使用同步:

Java代码 复制代码
  1. public class Singleton {   
  2.     // volatile关键字确保当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量   
  3.     private volatile static Singleton uniqueInstance;   
  4.     private Singleton() {   
  5.     }   
  6.   
  7.     public static Singleton getInSingleton() {   
  8.         if (uniqueInstance == null) {// 检查实例,如是不存在就进行同步代码区   
  9.             synchronized (Singleton.class) {// 对其进行锁,防止两个线程同时进入同步代码区   
  10.                 if (uniqueInstance == null) {// 双重检查,非常重要,如果两个同时访问的线程,当第一线程访问完同步代码区后,生成一个实例;当第二个已进入getInstance方法等待的线程进入同步代码区时,也会产生一个新的实例   
  11.                     uniqueInstance = new Singleton();   
  12.                 }   
  13.             }   
  14.         }   
  15.         return uniqueInstance;   
  16.     }   
  17.     // ...Remainder omitted   
  18. }  
public class Singleton {
	// volatile关键字确保当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量
	private volatile static Singleton uniqueInstance;
	private Singleton() {
	}

	public static Singleton getInSingleton() {
		if (uniqueInstance == null) {// 检查实例,如是不存在就进行同步代码区
			synchronized (Singleton.class) {// 对其进行锁,防止两个线程同时进入同步代码区
				if (uniqueInstance == null) {// 双重检查,非常重要,如果两个同时访问的线程,当第一线程访问完同步代码区后,生成一个实例;当第二个已进入getInstance方法等待的线程进入同步代码区时,也会产生一个新的实例
					uniqueInstance = new Singleton();
				}
			}
		}
		return uniqueInstance;
	}
	// ...Remainder omitted
}

对于双重检查加锁(Double-Checked Locking)有一篇文章解释的很深入:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

三。Sington类的序列化

为了使Singleton类变成可序列化的(serializable),仅仅实现Serializable接口是不够的。为了维护 Singleton的单例性,你必须给Singleton类提供一个readResolve方法,否则的话,一个序列化的实例,每次反序列化的时候都会产生一个新的实例。Singleton 也不会例外。

如下所示:

Java代码 复制代码
  1. import java.io.FileInputStream;   
  2. import java.io.FileOutputStream;   
  3. import java.io.ObjectInputStream;   
  4. import java.io.ObjectOutputStream;   
  5. import java.io.ObjectStreamException;   
  6. import java.io.Serializable;   
  7.   
  8. //Singleton with final field   
  9. public class Singleton implements Serializable{   
  10.   
  11.     private static final long serialVersionUID = 5765648836796281035L;   
  12.     public static final Singleton uniqueInstance = new Singleton();   
  13.     private Singleton(){   
  14.     }   
  15.     //...Remainder omitted   
  16.     public static void main(String[] args) throws Exception{   
  17.         //序列化   
  18.          ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\Singleton.obj"));   
  19.          Singleton singleton = Singleton.uniqueInstance;            
  20.          objectOutputStream.writeObject(singleton);   
  21.          objectOutputStream.close();   
  22.          //反序列化   
  23.          ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\Singleton.obj"));   
  24.          Singleton singleton2 = (Singleton)objectInputStream.readObject();   
  25.          objectInputStream.close();   
  26.          //比较是否原来的实例   
  27.          System.out.println(singleton==singleton2);   
  28.    }    
  29. }  
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;

//Singleton with final field
public class Singleton implements Serializable{

	private static final long serialVersionUID = 5765648836796281035L;
	public static final Singleton uniqueInstance = new Singleton();
	private Singleton(){
	}
	//...Remainder omitted
	public static void main(String[] args) throws Exception{
        //序列化
         ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\Singleton.obj"));
         Singleton singleton = Singleton.uniqueInstance;         
         objectOutputStream.writeObject(singleton);
         objectOutputStream.close();
         //反序列化
         ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\Singleton.obj"));
         Singleton singleton2 = (Singleton)objectInputStream.readObject();
         objectInputStream.close();
         //比较是否原来的实例
         System.out.println(singleton==singleton2);
   } 
}

 输出结果为:false

解决方法是为Singleton类增加readResolve()方法:

Java代码 复制代码
  1. //readResolve 方法维持了Singleton的单例属性   
  2.     private Object readResolve() throws ObjectStreamException{   
  3.         return uniqueInstance;   
  4.     }  
//readResolve 方法维持了Singleton的单例属性
	private Object readResolve() throws ObjectStreamException{
        return uniqueInstance;
    }

 再进行测试:输出结果为true

 

反序列化之后新创建的对象会先调用此方法,该方法返回的对象引用被返回,取代了新创建的对象。本质上,该方法忽略了新建对象,仍然返回类初始化时创建的那个实例。

分享到:
评论

相关推荐

    43丨单例模式(下):如何设计实现一个集群环境下的分布式单例模式?1

    单例模式是一种设计模式,旨在确保一个类只有一个实例,并提供全局访问点。在单例模式中,类的构造函数是私有的,防止外部直接创建对象,而是通过静态方法获取该类的唯一实例。单例模式的唯一性通常是在进程范围内,...

    设计模式单例模式和工厂模式综合应用

    "设计模式单例模式和工厂模式综合应用"的主题聚焦于两种常用的设计模式:单例模式和工厂模式,并探讨它们如何协同工作来实现高效、灵活的代码结构。这个主题尤其适用于Java编程语言,因为Java的面向对象特性使得设计...

    设计模式之单例模式(结合工厂模式)

    单例模式是软件设计模式中的一种经典模式,它保证了类只有一个实例存在,并提供一个全局访问点。在Java等面向对象编程语言中,单例模式常用于管理共享资源,如数据库连接池、线程池或者配置文件等。结合工厂模式,...

    java单例模式实例

    单例模式是软件设计模式中的一种经典模式,用于确保一个类只有一个实例,并提供一个全局访问点。在Java中,有多种实现单例模式的方法,每种都有其特点和适用场景。接下来,我们将深入探讨这些实现方式。 首先,我们...

    使用C++11实现线程安全的单例模式

    在C++编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。线程安全的单例模式在多线程环境下尤其重要,因为不正确的实现可能导致多个线程创建多个实例,这违反了单例模式...

    设计模式——单例模式

    **设计模式——单例模式** 在软件工程中,设计模式是一种在特定场景下解决常见问题的标准方案,可以被复用并提升代码质量。单例模式是设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。这种模式...

    C#单例模式详解 C#单例模式详解C#单例模式详解

    单例模式是软件设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。在C#中,单例模式常用于管理共享资源或控制类的实例化过程,以提高性能、节约系统资源,特别是在整个应用程序生命周期内只需要一...

    7种单例模式

    单例模式是软件设计模式中的一种经典模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下非常有用,比如控制共享资源、管理配置对象等。下面将详细介绍七种常见的单例模式实现...

    使用单例模式实现计数器

    其中,单例模式是一种非常经典且常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。在C#中,我们可以利用单例模式来创建一个计数器类,以确保在整个应用程序的生命周期内,计数器只...

    使用单例模式创建学生管理系统(饿汉式、懒汉式)

    单例模式是软件设计模式中的一种,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。在Java或类似的面向对象编程语言中,单例模式常用于管理共享资源,如数据库连接池、线程池或者配置文件等。在这个...

    c++单例模式线程日志类

    在C++编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在这个特定的场景中,我们讨论的是一个实现了单例模式的日志类,该类专为多线程环境设计,具备日志等级控制、...

    单例模式实现mdi界面子窗体控制

    首先向关注过我这个系列...这立刻让我想到了最常用也是最简单最容易理解的一个设计模式 单例模式 何为 单例模式 ? 故名思议 即 让 类 永远都只能有一个实例。 由于 示例代码 比较简单 我也加了注释,这里就不在赘述

    几种单例模式demo

    单例模式是软件设计模式中的一种,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下都非常有用,比如控制资源的唯一性、管理共享配置或者创建昂贵的对象时避免频繁创建销毁。 ...

    Java 单例模式 工具类

    Java中的单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供全局访问点。在Java编程中,单例模式常用于控制资源的访问,比如数据库连接池、线程池或者日志对象等。本篇文章将深入探讨如何在Java中...

    单例模式详解~~单例模式详解~~

    单例模式是一种设计模式,它的主要目标是确保一个类只有一个实例,并提供一个全局访问点。在软件工程中,单例模式常用于控制资源的共享,比如数据库连接池、线程池或者日志系统等,这些资源通常需要全局唯一且高效地...

    单例模式(singleton)

    单例模式是软件设计模式中的一种,它的核心思想是确保一个类在整个系统中只有一个实例,并提供一个全局访问点。在Java或类似编程语言中,单例模式常常被用来管理资源,比如数据库连接、线程池或者配置信息,因为这些...

    Qt qml Singleton 单例模式

    在Qt的Qml环境中,单例模式是一种设计模式,它允许在整个应用程序中创建一个全局访问点,确保某个类只有一个实例存在。这样的设计模式在需要共享数据或者服务时非常有用,避免了多处创建相同对象导致的数据不一致或...

    设计模式之单例模式源码demo

    单例模式是软件设计模式中的经典模式之一,其主要目的是控制类的实例化过程,确保在应用程序的整个生命周期中,某个类只有一个实例存在。这样的设计通常适用于那些需要频繁创建和销毁,但资源消耗较大的对象,如...

    23钟设计模式之单例模式

    单例模式是一种常用的设计模式,它的核心思想是在整个应用程序中,一个类只能有一个实例存在。单例模式常用于控制资源的共享,例如数据库连接池、日志服务等。单例模式有多种实现方式,常见的包括懒汉式、饿汉式以及...

Global site tag (gtag.js) - Google Analytics