`
Coraction
  • 浏览: 6339 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java的单例模式(转)

阅读更多

【转自:www.chenyudong.com/archives/java-singleton.html】作者:东东东 陈煜东

 

在设计模式中,单例模式(Singleton)是最长见得一种设计模式之一。什么是单例模式呢?就是在整个系统中,只有一个唯一存在的实例。这样的情况可以干什么用呢?比如可以统计网站的访问量,一些连接池(数据库连接池等)。

一个最简单的单例模式 – 饿汉模式

那么怎么能保证只有一个对象的存在呢?首先得有一个static的实例,这个方法保证了一个class只有一个实例。还得防止外界使用构造器来new一个实例。

1
2
3
4
5
//一个没有封装的单例模式
public class Singleton {
    public static final Singleton singleton = new Singleton();
    private Singleton(){}
}

外界就可以使用Singleton.singleton这样的方法来调用了。但是这样存在的问题就是分装不够好,添加一个方法,返回singleton的引用。如下

1
2
3
4
5
6
//最简单的封装单例改进版。饿汉模式
public class Singleton {
    public static final Singleton singleton = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){ return singleton; }
}

当代码写到这,我们终于可以松一口气了,原来单例模式也很简单呀,就这么几行的代码。对,其实这个是最简单的一种方式,能够应付大部分的场景的。

不过,其他class在引用Singleton而不使用的时候,虚拟机会自动加载这个类,并且实例化这个对象(这点知道Java虚拟机的类加载就会了解那么一些)。于是我们就有了下面的写法。

延迟实例化(懒汉模式) – 在调用时进行实例化

经常能看见其他的单例模式会教下面的代码,这样的人估计是从《设计模式 – 可复用面向对象软件的基础》那本书看来的。这本书使用的是C++语言写的,然后就将其转到了Java平台来。

首先他们会说我们的代码应该先这样,在调用的时候,发现为null,再进行实例化该类。

01
02
03
04
05
06
07
08
09
10
public class Singleton {
    public static final Singleton singleton = null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(singleton == null){ //如果singleton为空,表明未实例化
           singleton = new Singleton();
        }
        return singleton;
    }
}

然后他们还会说,这样在多线程的情况下会出现这样的情况:两个进程都进入到if (singleton == null),于是两个线程都对这个进行实例化,这样就出问题啦。

所以他们又说应该使用synchronize关键字,在实例化之前,进行加锁行为。于是又产生了一下的代码

01
02
03
04
05
06
07
08
09
10
11
12
13
public class Singleton {
    public static final Singleton singleton = null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(singleton == null){ //如果singleton为空,表明未实例化
           synchronize (Singleton.class){
               if( singleton == null ) { // double check 进来判断后再实例化。
                   singleton = new Singleton();
               }
        }
        return singleton;
    }
}

他们就会说,他们的这个代码使用了double check (双重检测),以防止这样的情况:当两个线程执行完第一个singleton == null 后等待锁, 其中一个线程获得锁并进入synchronize后,实例化了,然后退出释放锁,另外一个线程获得锁,进入又想实例化,会判断是否进行实例化了,如果存在,就不进行实例化了。

他们会说,这样的代码perfect,很完美,既解决了多线程带来的问题,又解决了延迟实例化的方式。

我觉得这样的代码只是将C++版的单例模式复制到Java平台,没有Java的特色。第一个就是一个Java特色的代码,它解决了多线程的问题,因为JLS(Java Language Specification)中规定了一个类(Singleton.class)只会被初始化一次,但是不能解决延迟实例化的情况。如需要延迟实例化,可以看下面的方法,使用内部类来实现。

使用内部类的单例模式 (懒汉模式)

刚才说了,第一个不能解决延迟实例化Singleton对象的问题。所以我们使用内部类来进行,看看代码。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
//一个延迟实例化的内部类的单例模式
public final class Singleton {
 
    //一个内部类的容器,调用getInstance时,JVM加载这个类
    private static final class SingletonHolder {
        static final Singleton singleton =  new Singleton();
    }
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        return SingletonHolder.singleton;
    }
 }

我们来看看这个代码。首先,其他类在引用这个Singleton的类时,只是新建了一个引用,并没有开辟一个的堆空间存放(对象所在的内存空间)。接着,当使用Singleton.getInstance()方法后,Java虚拟机(JVM)会加载SingletonHolder.class(JLS规定每个class对象只能被初始化一次),并实例化一个Singleton对象。

这样做就可以解决前面说的对线程多次实例化对象延迟实例化对象的问题了。

缺点:不过你会使用这样复杂的方式嘛?代码那么多。只是为了延迟一个对象的实例化,引入另外一个class。就为了延迟那么一次对象延迟的实例化,延缓Java的heap堆内存。为此付出的代价是引入一个class,需要在Java的另外一个内存空间(Java PermGen 永久代内存,这块内存是虚拟机加载class文件存放的位置)占用一个大块的空间。

还存在的一些问题

好了,在许多情况其实用第一种方法就差不多可以了。在特殊情况下,还是存在着一些问题。

用反射生成对象

如果使用Java的反射机制来生成对象的话,那么单例模式就会被破坏。

1
2
3
4
5
//使用反射破坏单例模式
Class c = Class.forName(Singleton.class.getName());
Constructor constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton singleton = (Singleton)ct.newInstance();

对于用反射破坏单例模式的,是不对其进行代码保护的,即由此造成的后果,由写反射的构建单例实例的人负责。所以我们就不用担心反射带来的问题了。

分布式上,解决单例模式

对于分布式上的单例模式,应该使用RMI(Remote Method Invocation 远程方法调用)来进行。或者使用web serivce,在单个服务器上存在单例,其他的机器使用SOAP协议进行访问。

不同的CLASSLOADER(类加载器)加载SINGLETON

在不同的ClassLoader加载Singleton,他们是不一样的。就像在不同的package中相同的类名,他们是不同的类。同样的,在不同的ClassLoader上加载的类,他们尽管代码一样,还是属于不同的类。

这样,需要自己写classloader,保证Singleton.class的加载唯一。

参考文章:http://www.oschina.net/question/9709_102019

单例模式的对象是否会被JVM回收?

对于这个问题,在还没实例化单例的时候,对象不存在,单实例化后,那么singleton的引用就存在的,只要Singleton.class存在虚拟机中。那么什么时候Singleton.class会被回收呢?对于这个问题,牵扯了许多的问题。因为Singleton对象存在,所以Singleton.class就也存在,这样形成了相互依赖,所以不会被JVM垃圾回收。

网上有个文章验证了他的想法 http://blog.csdn.net/zhengzhb/article/details/7331354

使用反序列化生成对象

如果你的Singleton序列化了,那么通过反序列化方式可以生成一个对象。通过增加readResolve方法来解决。如下

1
2
3
4
5
6
7
//最简单的封装单例改进版。饿汉模式。序列化及反序列化解决
public class Singleton implements java.io.Serializable {
    public static final Singleton singleton = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){ return singleton; }
    private Object readResolve() {  return singleton;   }
}

使用CLONE()克隆对象

Object的clone()方法默认是抛出CloneNotSupportedException异常的,所以只要不覆盖该方法,调用的时候,也会抛出异常。

effective java作者提到用enum枚举来实现单例模式。

扩展阅读:使用enum来进行Java的单例模式:http://coolxing.iteye.com/blog/1446648

分享到:
评论

相关推荐

    java单例模式实例

    在Java中,有多种实现单例模式的方法,每种都有其特点和适用场景。接下来,我们将深入探讨这些实现方式。 首先,我们来看**懒汉式(Lazy Initialization)**。这种实现方式是在类被首次请求时才创建单例对象,延迟...

    Java 单例模式.pptx

    ### Java 单例模式详解 #### 一、什么是单例模式? 单例模式是一种常用的软件设计模式,在这种模式中,一个类只能拥有一个实例,并且该类必须自行创建并提供这个实例。通常,单例模式用于确保某个类在整个应用程序...

    Java 单例模式 工具类

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

    Java 单例模式 懒汉模式

    Java 单例模式 懒汉模式 //懒汉式 多线程中不可以保证是一个对象

    Java SE程序 单例模式

    Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式Java SE程序 单例模式...

    使用Java单例模式实现一个简单的日志记录器.txt

    ### 使用Java单例模式实现一个简单的日志记录器 #### 一、单例模式简介 单例模式是一种常用的软件设计模式,在该模式中,一个类只能创建一个实例,并且提供了一个全局访问点来访问该实例。单例模式的主要优点包括...

    Java单例模式设计

    Java单例模式是一种常用的设计模式,它保证一个类只有一个实例,并提供全局访问点。这种模式在需要频繁创建和销毁对象的场景中,或者当对象昂贵时(如数据库连接),能够节省系统资源,提高效率。本篇文章将深入探讨...

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

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

    Java单例模式应用研究.pdf

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

    Java单例模式深入理解

    Java单例模式是一种设计模式,它允许在程序中创建唯一一个类实例,通常用于管理共享资源,例如数据库连接、线程池或者配置对象等。单例模式的核心在于限制类的构造函数,确保类只能被初始化一次,从而实现全局唯一的...

    java 获取 配置文件 属性 单例模式 高效加载

    通过单例模式实例化获取propertyUtil 工具包实例,高效加载配置文件,java语言编写。通过单例模式实例化获取propertyUtil 工具包实例,高效加载配置文件,java语言编写。通过单例模式实例化获取propertyUtil 工具包...

    实用Java的单例模式,实用于Java学习者

    实用Java的单例模式,实用于Java学习者 单例模式 单例模式

    Java实现多种单例模式

    在Java编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。这种模式在需要频繁创建和销毁对象的场景中尤其有用,因为它可以节省系统资源并确保对象间的协调一致。以下是...

    java单例模式及实现

    Java单例模式及实现 Java单例模式是一种常见的设计模式,确保某一个类只有一个实例,而且向这个系统提供这个实例。单例模式可以分为三种:懒汉式单例、饿汉式单例、登记式单例。 单例模式的要点 1. 某个类只能有...

    java单例模式连接数据库源码

    Java单例模式是一种设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在数据库连接管理中,使用单例模式能有效控制资源,避免频繁创建和关闭数据库连接导致的性能损失和资源浪费。以下是对Java单例模式...

    java单例模式的例子

    Java单例模式是一种常见的设计模式,它在软件工程中用于控制类的实例化过程,确保一个类只有一个实例,并提供一个全局访问点。这种模式在系统资源管理、缓存、日志记录等方面应用广泛。下面我们将深入探讨Java单例...

Global site tag (gtag.js) - Google Analytics