`
zhou_zhihao
  • 浏览: 57230 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Singleton-单例模式

 
阅读更多


Singleton-单例模式

1.单例简介

在你确信你的类对应的对象或者实例只需要一个的情况下,单例模式就比较适用。如数据库线程池、读配置文件等都是原型的示例。

 

2.单例实现模式

Java中static的特性:

  • static变量在类装载的时候进行初始化

  • 多个实例的static变量会共享同一块内存区域

利用static的特性,我们就能够控制值创造一个实例。

 


2.1 饿汉式(预先加载)

饿汉式最常见的实现方式: 


1
2
3
4
5
6
7
8
9
10
11
public class Singleton         
{        
    private static Singleton instance = new Singleton();        
                   
    private Singleton (){}        
                   
    public static Singleton getInstance()         
    {        
        return instance;        
    }        
}

还有另外一种方式,区别不大: 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton         
{        
    private Singleton instance = null;        
                   
    static
    {        
        instance = new Singleton();        
    }        
                   
    private Singleton (){}        
                   
    public static Singleton getInstance()         
    {        
        return this.instance;        
    }        
}

优点:

1.线程安全

2.类加载的同时已经创建好一个静态实例,调用时反应快

缺点: 

资源利用效率不高,可能getInstance永远不会执行到

 

饿汉式的方式在一些场景中将无法使用,如实例的创建依赖于参数。




2.2 懒汉式(延迟加载)



1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton         
{        
    private static Singleton instance;        
    private Singleton (){}        
                   
    public static Singleton getInstance()         
    {        
        if (instance == null)         
        {        
            instance = new Singleton();        
        }        
        return instance;        
    }        
}

我们一般写懒汉式都是以上的方式,但是在多线程环境下,是不安全的,很容易导致在内存中创建两个实例,李逵李鬼,问题就来了。

于是,我们就很容易的想到为getInstance()方法加上synchronized标识符,这样就可以保证不会出现线程问题了,如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton        
{       
    private static Singleton instance;       
                 
    private Singleton (){}       
                 
    public static synchronized Singleton getInstance()        
    {       
        if (instance == null)        
        {       
            instance = new Singleton();       
        }       
        return instance;       
    }       
}

除了第一次调用的时候执行实例的生成之外,以后每次都是直接返回实例。那么后面每次调用getInstance()方法都会进行同步准备,会有很大的消耗,性能上很不划算。于是我们把代码改成如下的方式:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Singleton       
{       
    private static Singleton instance;       
                 
    private Singleton()       
    {       
    }       
                 
    public static Singleton getInstance()       
    {       
        synchronized (Singleton.class)       
        {       
            if (instance == null)       
            {       
                instance = new Singleton();       
            }       
        }       
        return instance;       
    }       
}

基本上按照以上方式把synchronized移到getInstance()方法中没有任何意义,每次调用还是需要进行同步。我们只是希望在第一次创建Singleton时进行同步,因此有了以下的方式,双重锁定检查(DCL).

  


2.3 双重锁定检查(DCL)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Singleton       
{       
    private static Singleton instance;       
                 
    private Singleton()       
    {       
    }       
                 
    public static Singleton getInstance()       
    {       
        if (instance != null)       
        {       
            synchronized (Singleton.class)       
            {       
                if (instance == null)       
                {       
                    instance = new Singleton();       
                }       
            }       
        }       
        return instance;       
    }       
}


看上去这样已经达到了我们的要求,但是这样就没有问题了么?

假设线程执行到了


1
instance = new Singleton();

这一句,这里看上去是一句话,但是实际上并不是一个原子操作(原子操作的含义是要么没有执行,要么执行完)。在高级语言中非原子操作很多,通过JVM的后台,这一句话做了大概三件事情:

  1. 给instance分配内存

  2. 初始化Singleton构造器

  3. 将instance实例指向分配的内存空间(instance非null)

但是,由于Java编译器是乱序执行的,以及JDK5之前的内存模型中缓存、寄存器到主内存回写的顺序,上面2,3的顺序无法保证。如果线程1执行时先执行3后执行2,正在3执行完,2未执行之际,线程2调用获得instance去使用,报错。如果通过调试去解决故障,估摸着一周都找不出错误所在。

 

此方法在一些语言中是可行的,如C语言。在JDK5之后,已经注意到此问题,调成了内存模型,并具体化了volatile,因此在JDK5之后版本,只需要将instance标识为volatile,就可以保证执行的顺序,具体如下所示:

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Singleton       
{       
    private volatile static Singleton instance;       
                 
    private Singleton()       
    {       
    }       
                 
    public static Singleton getInstance()       
    {       
        if (instance != null)       
        {       
            synchronized (Singleton.class)       
            {       
                if (instance == null)       
                {       
                    instance = new Singleton();       
                }       
            }       
        }       
        return instance;       
    }       
}

volatile或多或少会影响一些性能,而且在JDK1.42之前无法使用,看一下单例其他实现方式。

 

2.4 静态内部类实现单例


1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton       
{       
    private static class SingletonHolder       
    {       
        private static final Singleton instance = new Singleton();       
    }       
                 
    public static Singleton getInstance()       
    {       
        return SingletonHolder.instance;       
    }       
}

    这种写法仍然使用了JVM本身的机制保证了线程安全的问题,由于SingletonHolder是私有的,除了getInstance()方法之外没有其他方法访问。这种也是懒汉式的。同时读取实例的时候不会进行同步,没有性能缺陷,也不依赖JDK的版本。

 

2.5 总结和扩展

其他单例的写法还有很多,如JDK5之后通过枚举的方式、使用本地线程(ThreadLocal)来处理并发以及保证一个线程内一个实例的实现、GOF原始例子中用注册方式、使用指定类加载器去应对多个加载器的实现等等,我们在做设计时,需要考虑可能的扩展和变化,也要避免无谓的提升设计、实现复杂度等。设计不足和过度设计都是危害,找到最合适的方式才是最好的。

 

目前单例模式介绍完,最后介绍一下其他途径屏蔽构造单例实例的方法:

  1. new 单例对象

    通过加入private或者protected的构造方法,就无法直接通过new的方式构造

  2. 反射构造单例对象

    反射时可以使用setAccessible方法来突破private的限制,这就需要在 ReflectPermission("suppressAccessChecks") 权限下使用安全管理器(SecurityManager)的checkPermission方法来限制这种突破。一般来说,不会真的去做这些事情,都是通过应用服务器进行后台配置实现。 

     

  3. 序列化构造单例对象(很少出现)

    如果单例需要实现Serializable接口,那么应当实现readResolve()方法来保证反序列化的时候得到原来的对象

基于以上,使用静态内部类的单例模式实现可能如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.Serializable;       
                 
public class Singleton implements Serializable       
{       
    private static final long serialVersionUID = 2382773738822334655L;       
                 
    private static class SingletonHolder       
    {       
        private static final Singleton instance = new Singleton();       
    }       
                 
    public static Singleton getInstance()       
    {       
        return SingletonHolder.instance;       
    }       
                 
    private Singleton()       
    {       
    }       
                 
    private Object readResolve()       
    {       
        return getInstance();       
    }       
}

到此为止,介绍了单例模式的JAVA实现,并介绍了各自的优缺点。单例模式是一种即简单又复杂的模式。

 

 2.6 参考

请不吝赐教。

@anthor ClumsyBirdZ


 

 

分享到:
评论

相关推荐

    java设计模式----单例模式

    单例模式是其中最常用的一种,它的核心思想是确保一个类只有一个实例,并提供全局访问点。单例模式的应用场景包括:控制资源的访问、管理复杂的初始化过程以及在系统中创建一个全局的配置对象等。 单例模式分为两种...

    Java设计模式-单例模式详解

    Java设计模式-单例模式详解 单例模式是 Java 设计模式中的一种常用的设计模式,旨在保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式的目的是为了保证在一个进程中,某个类有且仅有一个实例。 ...

    大话设计模式--Singleton(单例模式)

    单例模式是软件设计模式中的一种经典模式,它在许多场景下被广泛使用,尤其是在需要全局唯一实例的情况下。本文将深入探讨单例模式的概念、作用、实现方式以及其在实际编程中的应用。 单例模式的核心思想是确保一个...

    常见设计模式-单例模式

    设计模式-单例模式 单例模式是一种常见的设计模式,它的主要作用是确保在内存中仅创建一次对象,并提供一个全局访问点。单例模式有两种类型:饿汉类型和懒汉类型。 饿汉类型的单例模式是在类加载时创建对象,例如...

    ava常用设计模式-单例模式

    ava常用设计模式-单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,有以下特点: 1. 单例类只能有一个实例。 2. 单例类必须自己创建自己的唯一实例。 3. 单例类必须给所有其他对象提供这一...

    设计模式-单例模式

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

    Singleton pattern单例模式应用

    ### Singleton Pattern 单例模式应用详解 #### 一、单例模式概述 单例模式(Singleton Pattern)是一种常用的软件设计模式,在系统中确保某个类只有一个实例,并提供一个全局访问点。这种模式通常用于控制资源的...

    设计模式ForiOS-02-单例模式.pdf

    单例模式是一种软件设计模式,它的核心思想是确保一个类在整个应用程序中只有一个实例,并提供一个全局访问点来获取这个实例。这种模式在iOS开发中非常常见,因为许多系统类如UIApplication、NSFileManager等都采用...

    PHP5设计模式-单例模式

    单例模式是一种常用的设计模式,它在软件工程中扮演着控制类实例化过程的角色,确保一个类只有一个实例,并提供全局访问点。这种模式在PHP中尤其常见,特别是在需要频繁实例化然后销毁的对象,或者需要控制共享资源...

    设计模式--单例模式java例子

    单例模式是软件设计模式中的一种经典模式,它在Java编程中被广泛使用。这个模式的主要目的是确保一个类只有一个实例,并提供一个全局访问点。这样做的好处在于可以控制实例的数量,减少资源消耗,同时便于协调整个...

    设计模式-单例模式(讲解及其实现代码)

    单例模式是软件设计模式中的一种,它保证一个类只有一个实例,并提供全局访问点。这种模式在许多场景下非常有用,比如控制共享资源、管理系统级别的对象,如数据库连接池或者线程池等。单例模式的核心在于限制类的...

    java-单例模式几种写法

    单例模式是软件设计模式中的一种,用于控制类的实例化过程,确保一个类只有一个实例,并提供全局访问点。在Java中,实现单例模式有多种方法,每种方法都有其特点和适用场景。以下是对这六种常见单例模式实现方式的...

    IOS应用源码Demo-单例模式-毕设学习.zip

    在"IOS应用源码Demo-单例模式-毕设学习.zip"这个压缩包中,你可以找到一个关于单例模式的实际示例,这对于正在进行毕业设计或者论文写作的iOS开发者来说是一份宝贵的学习资料。 单例模式的主要特点和优势包括: 1....

    c++设计模式-单例模式

    **单例模式**是软件设计模式中的一种,属于创建型模式。在C++中,单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于管理共享资源,如数据库连接、日志系统或者配置文件等。单例模式的主要...

    C#设计模式学习与演变过程-2-单例模式

    单例模式是软件设计模式中的一种经典模式,它旨在确保一个类只有一个实例,并提供一个全局访问点。在C#编程中,单例模式的应用广泛,特别是在需要频繁实例化然后销毁的对象,或者创建对象需要消耗大量资源的情况下,...

    Qt qml Singleton 单例模式

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

    乐在其中设计模式(C#)-单例模式(SingletonPattern)[收集].pdf

    《乐在其中设计模式(C#) - 单例模式(Singleton Pattern)》 单例模式是一种常用的设计模式,其核心思想在于确保一个类在整个应用程序的生命周期中只存在一个实例,并提供一个全局访问点来获取这个唯一的实例。在C#中...

    优秀的设计模式示例-单例模式

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

    Java-设计模式-单例模式-实现源码(简单实现、双重检查锁、静态内部类、枚举类)

    单例模式是软件设计模式中的一种经典模式,其主要目的是保证一个类只有一个实例,并提供一个全局访问点。在Java中,有多种实现单例模式的方法,包括简单实现、双重检查锁定(Double-Checked Locking)、静态内部类和...

Global site tag (gtag.js) - Google Analytics