`

主题:java单例模式(Singleton pattern)----转载

阅读更多
一、定义

单例模式(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代码
//Singleton with final field  
public class Singleton {  
    public static final Singleton uniqueInstance = new Singleton();  
    private Singleton(){  
    }  
    //...Remainder omitted  


//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代码
//Singleton with static factory  
public class Singleton {  
    private static Singleton uniqueInstance = new Singleton();  
    private Singleton(){  
    }  
    public static Singleton getInSingleton(){  
        return uniqueInstance;  
    }  
    //...Remainder omitted  


//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代码
public class Singleton {  
    private static Singleton uniqueInstance ;  
    private Singleton(){  
    }  
    public static Singleton getInSingleton(){  
        if(uniqueInstance == null){  
            uniqueInstance = new Singleton();  
        }  
        return uniqueInstance;  
    }  
    //...Remainder omitted  


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代码
public class Singleton {  
    private static Singleton uniqueInstance ;  
    private Singleton(){  
    }  
    public synchronized static Singleton getInSingleton(){  
        if(uniqueInstance == null){  
            uniqueInstance = new Singleton();  
        }  
        return uniqueInstance;  
    }  
    //...Remainder omitted  


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代码
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  


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代码
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);  
   }   


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代码
//readResolve 方法维持了Singleton的单例属性  
    private Object readResolve() throws ObjectStreamException{  
        return uniqueInstance;  
    } 

//readResolve 方法维持了Singleton的单例属性
private Object readResolve() throws ObjectStreamException{
        return uniqueInstance;
    } 再进行测试:输出结果为true



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


分享到:
评论

相关推荐

    单例模式 Singleton Pattern

    ### 单例模式 Singleton Pattern #### 概述 单例模式是一种常见的设计模式,属于创建型模式之一。这种模式的核心在于确保某个类只有一个实例存在,并且提供一个全局访问点来获取该实例。单例模式在Java开发中尤其...

    Singleton pattern单例模式应用

    在给定的部分内容中,虽然没有直接展示单例模式的实现,但我们可以基于其原理来构建一个简单的Java单例模式例子。下面是一个典型的饿汉式单例模式的Java实现: ```java public class Singleton { // 私有构造函数...

    创建型模式之单例模式(Singleton Pattern)

    单例模式是软件设计模式中的一种,属于创建型模式,它的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下都非常有用,例如管理共享资源、配置对象或者缓存服务等。 单例模式的核心...

    设计模式 之 “单例模式[Singleton Pattern]”

    **单例模式(Singleton Pattern)**是软件设计模式中的一种基础模式,它的核心思想是确保一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下非常有用,比如配置管理、线程池、数据库连接池等,这些都...

    JAVA单例模式的几种实现方法

    ### JAVA单例模式的几种实现方法 #### 一、饿汉式单例类 饿汉式单例类是在类初始化时就已经完成了实例化的操作。这种实现方式简单且线程安全,因为实例化过程是在编译期间完成的,不会受到多线程的影响。 **代码...

    单例模式源码java-DesignPattern:在个人自学阶段的23种设计模式代码的全部实现,全部使用Java编写,其中还包括各个设计模式在

    单例模式源码java DesignPattern 在个人自学阶段的23种设计模式代码的全部实现,全部使用Java编写,其中还包括各个设计模式在源码中的使用,每种设计模式都举了一个简单的小例子来进行实现,并加以注释 包名解释 一...

    设计模式-Java单例模式的各种实现实例

    单例模式(Singleton Pattern)是一种常用的软件设计模式,它保证一个类仅有一个实例,并提供一个访问该实例的全局访问点。这种模式通常用于需要频繁地创建和销毁的对象,以减少系统性能开销。 单例模式的优点: ...

    java设计模式---诙谐易懂版

    例如,代理模式(Proxy Pattern)、单例模式(Singleton Pattern)、工厂方法模式(Factory Method Pattern)、抽象工厂模式(Abstract Factory Pattern)、适配器模式(Adapter Pattern)、模板方法模式(Template ...

    Java单例模式应用研究.pdf

    ### Java单例模式应用研究 #### 一、单例模式概述 单例模式(Singleton Pattern)作为一种最基本的创建型设计模式,其主要目的是控制一个类的实例化过程,确保在整个应用程序中仅存在一个实例,并且该实例能够被全局...

    单例模式Singleton

    单例模式(Singleton Pattern)是一种常用的软件设计模式,它的核心思想是确保一个类在整个应用程序中只有一个实例存在,并提供一个全局访问点来获取这个实例。这种模式在很多场景下非常有用,比如管理系统资源、...

    java:采用单例模式计算全年有多少个周,并列出所有周和每一周所对应的时间段(内含注释)

    单例模式 (Singleton Pattern) **定义**: 单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。 **实现方式**: - **懒汉式**:在首次调用 `getInstance` 方法时创建实例。 - *...

    java-thread-safe-singleton-pattern.rar_starvation

    在Java单例模式中,延迟加载可以提高程序的运行效率,因为它避免了不必要的初始化开销。常见的延迟加载单例实现有懒汉式(Lazy Initialization)和饿汉式(Eager Initialization)。但是,简单的懒汉式单例在多线程...

    java设计模式之单例模式详解

    单例模式(Singleton Pattern)是面向对象设计模式中的一种,属于创建型模式。它确保一个类仅有一个实例,并提供一个全局访问点来访问该实例。单例模式的核心在于控制类的实例化过程,保证在任何情况下都只会创建一...

    design-pattern-java.pdf

    工厂三兄弟之抽象工厂模式(二) 工厂三兄弟之抽象工厂模式(三) 工厂三兄弟之抽象工厂模式(四) 工厂三兄弟之抽象工厂模式(五) 单例模式-Singleton Pattern 确保对象的唯一性——单例模式 (一) 确保对象的...

    JAVA design pattern-java设计模式

    在这个名为"JAVA design pattern-java设计模式"的CHM文件中,我们可能找到了关于如何在Java开发中应用设计模式的详细信息。 设计模式通常分为三大类:创建型、结构型和行为型。创建型模式关注对象的创建,如单例...

    java之 ------ 几种常见的简单设计模式

    #### 单例模式(Singleton Pattern) 单例模式是一种确保类只有一个实例,并提供一个全局访问点的设计模式。它的核心目标是限制对象的创建数量,保证在整个应用程序中只存在一个实例,这对于资源管理和控制非常重要...

    JAVA单例模式应用研究

    ### JAVA单例模式应用研究 #### 一、单例模式概述 单例模式(Singleton Pattern)作为一种最常见的创建型设计模式之一,在软件开发中扮演着重要角色。它主要应用于需要对系统资源进行统一管理和控制的场景,例如...

    JAVA设计模式中的单例模式

    其中,单例模式(Singleton Pattern)是一种常用的创建型模式,其核心在于确保一个类只有一个实例,并提供一个全局访问点。这种模式在多种场景下非常有用,比如管理共享资源、控制并发访问等。 #### 单例模式的重要...

    2 单例模式-MOOC课程内容.pdf

    单例模式是软件设计模式中的一种,属于创建型模式,但有时被称作“非创建型模式”。在单例模式中,设计者的目标是确保一个类只有一个实例,并且提供一个全局访问点给这个实例。在多线程和并发环境中,单例模式的实现...

    设计模式_单例模式.zip

    以下是一个简单的Java单例模式实现: ```java public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance =...

Global site tag (gtag.js) - Google Analytics