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

java Singleton 几种方式解析

阅读更多

概念:

在Java应用程序中,一个类Class只有一个实例存在

运用:

1)系统资源,如文件路径,数据库链接,系统常量等

2)全局状态化类,类似AutomicInteger的使用

优缺点:

1)节省内存有利于垃圾回收

2)只能使用在特定的环境下,受限制于JVM和容器

     单例作用范围的前提是在一个ClassLoad下。所以像分布式应用EJB就要用其它的方式来解决单例问题。

Demo:

分别列出多种实现方式,各分析其优缺点

1)静态成员直接初始化,或者在静态代码块初始化都可以


    class Singleton{  
        private Singleton(){}  
        private static final Singleton singleton = new Singleton();  
        public static Singleton getInstance(){return singleton;}  
    }  
 

      该实现只要在一个ClassLoad下就会提供一个对象的单例。但是美中不足的是,不管该资源是否被请求,它都会创建一个对象,占用jvm内存。从lazy initialization思想出发,出现了下2的写法

2)根据lazy initialization思想,使用到时才初始化。

    class Singleton{  
        private Singleton(){}  
        private static Singleton singleton ;  
        public static synchronized Singleton getInstance(){  
            if(singleton==null)  
                singleton = new Singleton();  
            return singleton;         
        }     
    }  
 

       该实现方法加了同步锁,可以有效防止多线程在执行getInstance方法得到2个对象。

缺点:只有在第一次调用的时候,才会出现生成2个对象,才必须要求同步。而一旦singleton 不为null,系统依旧花费同步锁开销,有点得不偿失。

因此再改进出现写法3

3)在2的基础上改进,改进标准:尽量减少锁资源(主要体现在执行时间,所占内存等)


    class Singleton{  
        private Singleton(){}  
        private static Singleton singleton ;  
        public static Singleton getInstance(){  
            if(singleton==null)//1  
                synchronized(Singleton.class){//2  
                    singleton = new Singleton();//3  
                }  
            return singleton;         
        }     
    }  
 

这种写法减少了锁开销,但是在如下情况,却创建了2个对象:

a:线程1执行到1挂起,线程1认为singleton为null

b:线程2执行到1挂起,线程2认为singleton为null

c:线程1被唤醒执行synchronized块代码,走完创建了一个对象

d:线程2被唤醒执行synchronized块代码,走完创建了另一个对象

所以看出这种写法,并不完美。

4)为了解决3存在的问题,引入双重检查锁定


    public static Singleton getInstance(){  
            if(singleton==null)//1  
                synchronized(Singleton.class){//2  
                    if(singleton==null)//3  
                        singleton = new Singleton();//4  
                }  
            return singleton;         
        }  
 

      在同步锁代码块内部,再判断一次对象是否为null,为null才创建对象。这种写法已经接近完美:

a:线程1执行到1,已经进入synchronized的时候,线程挂起,线程1占有Singleton.class资源锁;

b:线程2执行到1,当它准备synchronized块时,因为Singleton.class被占用,线程2阻塞;

c:线程1被唤醒,判断出对象为null,执行完创建一个对象

d:线程2被唤醒,判断出对象不为null,不执行创建语句

      如此分析,发现似乎没问题。

      但是实际上并不能保证它在单处理器或多处理器上正确运行;

      问题就出现在singleton = new Singleton()这一行代码。它可以简单的分成如下三个步骤:

mem= singleton();//1

instance = mem;//2

ctorSingleton(instance);//3

       这行代码先在内存开辟空间,赋给singleton的引用,然后执行new 初始化数据,但是注意初始化是要消耗时间。如果此时线程3在执行步骤1的时候,发现singleton 为非null,就直接返回,那么线程3返回的其实是一个没构造完成的对象。

      我们期望1,2,3 按照反序执行,但是实际jvm内存模型,并没有明确的有序指定。

      这归咎于java的平台的内存模型允许“无序写入”。

5)在4的基础上引入volatile

代码如下:


 

    class Singleton{  
        private Singleton(){}  
        private static volatile Singleton singleton ;  
        public static Singleton getInstance(){  
            if(singleton==null)//1  
                synchronized(Singleton.class){//2  
                    if(singleton==null)//3  
                        singleton = new Singleton();  
                }  
            return singleton;         
        }     
    }  
 

       Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。

而volatile使用时有明确的规定:

  1.       对变量的写操作不依赖于当前值;
  2.       该变量没有包含在具有其他变量的不变式中;

—— 只有在状态真正独立于程序内其他内容时才能使用 volatile。

但是5的写法,虽然理论上似乎可以解决无序写入问题。实际上并非如此。

(我个人觉得这里对volatile语法说的不够详细,想知道详细的可以看这篇转帖Java 理论与实践: 正确使用 Volatile 变量

小结:

1)使用同步锁方法,内部锁存在不安全。

2)静态成员直接初始化。

分享到:
评论

相关推荐

    Singleton 单例模式的介绍以及解析

    为了解决这个问题,我们可以采用以下几种策略: 1. **懒汉式(线程不安全)**:在首次调用时才初始化单例,但这种方式在多线程环境下可能不安全。两个线程可能同时进入if语句块,分别创建单例,从而产生多个实例。 ...

    JAVA中几种设计模式的例子

    在Java编程领域,设计模式是一种经过验证的、通用的解决方案,用于解决软件设计中常见的问题。以下是关于这七种设计模式的详细解释和实例: 1. 单例模式(Singleton): 单例模式确保一个类只有一个实例,并提供一...

    Java设计模式之单态模式(Singleton模式)介绍

    单态模式(Singleton模式)是Java设计模式中的一种,它的主要目标是确保一个类在整个应用程序中只有一个实例存在。这种模式的应用场景广泛,特别是在需要全局共享的资源管理、配置中心、日志服务等场合非常常见。 ...

    java精典编程100例 12

    在Java中,实现单例模式主要有以下几种方法: 1. **懒汉式**:也称为延迟加载模式,即在需要的时候才创建单例对象。 2. **饿汉式**:在类装载时就创建单例对象。 3. **双重检查锁定**:一种高效的懒汉式单例实现...

    JAVA面试设计模式.ppt

    以下是根据提供的内容对几种常见设计模式的详细说明: 1. **Singleton(单例模式)**: - 单例模式确保一个类只有一个实例,并提供全局访问点。这种模式常用于配置管理、线程池、缓存管理等场景。 - 形式一:饿汉...

    Java se 面试题.docx

    在Java中,实现单例主要有以下几种方式: - 饿汉式(Eager Initialization):在类加载时就初始化,确保单例对象的唯一性。这种方式线程安全,但可能会浪费内存,因为即使没有使用,单例也会被创建。 ```java ...

    java面试题.doc

    使用 Java 解析 XML 的方式有多种,例如使用 DOM、SAX 等解析器。 9. EJB 基本架构 EJB(Enterprise JavaBeans)是一种用于构建 enterprise 应用的 Java 技术。 10. 数字型校验 如何校验数字型?例如使用 ...

    java面试题及技巧4

    │ 164个完整Java代码.zip │ J2EE综合--Struts常见错误的全面汇总.txt │ java程序员面试资料.zip │ JAVA笔试题(上海释锐).pdf │ MIME简介.txt │ SCJP试题详解.pdf │ SQL面试题_心灵深处.htm │ Struts+...

    Android源码设计模式解析与实战.PDF(完整版)

    在Android开发过程中,经常使用的几种设计模式包括单例模式、观察者模式、工厂模式、装饰器模式等。每种模式都有其适用场景和独特优势,通过合理的运用,可以显著提升项目的质量和效率。 ### 单例模式详解 #### ...

    JAVA笔试题----------大唐软件

    XML解析是Java中的一种重要技术,用于解析XML文件。XML解析有多种方法,包括DOM解析、SAX解析等。 12. Open Source项目: Open Source项目是指免费、开放的软件项目,包括Apache、Spring、Hibernate等多种项目。 ...

    JAVA 23种模式

    以上就是对JAVA 23种模式中提及的几种设计模式的详细解析。这些模式在实际开发中有着广泛的应用,它们帮助开发者设计出更灵活、可维护的代码结构。了解并熟练应用这些模式,能极大地提高软件的可扩展性和可复用性。

    java核心面试

    为了保证在多线程环境中也能正确地实现单例模式,通常会采用以下几种方法: 1. **双重检查锁定(Double-Checked Locking)**: - 这种方式是在第一次实例化时进行同步,之后不再同步。 - 代码示例: ```java ...

    java设计模式ppt

    以下是对给定文件中提到的几种Java设计模式的详细解析。 #### 1. 单例模式 (Singleton Pattern) 单例模式是一种常用的对象创建型模式,其目的是确保一个类只有一个实例,并提供一个全局访问点。实现单例模式的关键...

    深圳各公司java面试题

    XML解析有哪几种?** 主要有DOM(Document Object Model)、SAX(Simple API for XML)和StAX(Streaming API for XML)三种。 **7. 简述AJAX** AJAX(Asynchronous JavaScript and XML)是一种在无需重新加载整个...

    从追MM谈Java的23种设计模式

    ### 从追MM谈Java的23种设计模式 #### 1. FACTORY(工厂模式) 工厂模式是一种常用的创建型设计模式,它提供了一个创建对象的接口,但允许子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。 **...

    23种设计模式的Java实现

    5. **其他设计模式**:除了以上几种,还有Builder模式(用于复杂对象的构建),Prototype模式(用于复制或克隆对象),Decorator模式(用于动态地给对象添加职责),Facade模式(提供一个统一的接口,用来访问子系统...

    多种设计模式的java实现方式

    除了以上几种模式,还包括以下设计模式的Java实现: 5. **AbstractFactory模式**:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。 6. **Builder模式**:将复杂对象的构建与它的表示分离,...

    JAVA设计模式中的单例模式

    根据部分文件内容的描述,我们可以看到几种常见的实现单例模式的方式: 1. **饿汉式** 这是最简单的一种实现方式,它在类加载时就完成了实例化,因此是线程安全的,但缺点是在类初始化时就完成了实例化,可能会...

    23种设计模式实例(JAVA实现)

    本文将详细解析标题为“23种设计模式实例(JAVA实现)”的压缩包中所涉及的设计模式,以及如何通过Java语言来实现它们。我们将主要关注以下几个设计模式:Adapter、Chain of Responsibility 和 Singleton,这些都是...

    Java面试宝典-经典

    解析XML文档有哪几种方式? 117 七. 流行的框架与新技术 117 1、谈谈你对Struts的理解。 117 2、谈谈你对Hibernate的理解。 118 3、AOP的作用。 118 4、你对Spring的理解。 118 5、谈谈Struts中的Action servlet。 ...

Global site tag (gtag.js) - Google Analytics