`

你真的了解单例模式么?

阅读更多

    单例模式,很多初学者认为单例模式很简单,并且认为自己已经掌握了这种设计模式。但事实上,你真的了解单例模式了么。

 

一,单例模式的5中写法。(回字的四种写法,哈哈。)

    1,懒汉式

          (1)线程不安全的懒汉式

    public class Singleton {  
        private static Singleton instance;  
        private Singleton (){}  
      
        public static Singleton getInstance() {  
        //线程a↓ 线程b↓ 
        if (instance == null) {
       //线程a 创建一个对象 ,线程b 在这又会创建一次
            instance = new Singleton();  
        }  
        return instance;  
        }  
    }  

            初学单例模式常见的写法,懒加载,但是这种写法在实际中应用很少,因为会有线程安全问题。

 

          (2)线程安全的懒汉式

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

    ​    ​    ​用加锁的方式避免了线程安全问题,但是每一次都会判断锁,性能较低。

 

    2,饿汉式

          (1)恶汉 No1

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

    ​    ​    ​在类初始化的时候就已经创建了单例对象,导致类的初始化 不仅仅 只有调用 getInstance方法。调用静态方法或者静态变量等等。

所以没有 懒加载 的效果。

 

          (2)恶汉 No2

public class Singleton {  
    private Singleton instance = null;  
    static {  
    instance = new Singleton();  
    }  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return this.instance;  
    }  
}

    ​    ​    ​和恶汉 No1 的效果一样。都是在类的初始化的时候去创建实例

 

    3,静态内部类

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}

    ​这种方式我很喜欢,既保证了线程安全又实现了懒加载。因为当外部的类初始化的时候,不会导致内部类的初始化。只有当调用getInstance()的时候,才会创建这个单利实例。懒加载的优点:当这个instance是一个非常消耗资源的对象。我们可以让它延迟加载。在合适的时机才会创建对象。

 

    4,枚举

public enum Singleton {
	INSTANCE;
	public void doSomeThing() {
		//do someThing
	}
}

    ​这种也是非常推荐的方式,枚举是JDK1.5的特性,所以用的比较少。这种方式代码简单,并且保证的线程安全,还能防止反序列化创建新的对象,但是也同事失去了懒加载的特性。

 

    5,双重校验锁

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

    ​这种写法,这种方式像是“线程安全的懒汉模式的增强版”比较麻烦。并且在JDK1.5之前,由于instance= new Singleton();这句话不是原子操作,这句话分为三步

    1.Singleton的实例分配内存。

    2.初始化Singleton的构造器

    3.instance对象指向分配的内存空间(注意到这步instance就非null了)。

由于java编译器out-of-order(允许乱序执行)以及JMM中Cache、寄存器到主内存回写顺序的规定,上面的第二点和第三点的顺序是无法保证的。也就是 不一定是 1,2,3的顺序,如过是1,3,2. 在3执行完的时候,instance就非null了。此时切换到第二个线程,直接拿走这个实例使用,就会报错。但是在JDK1.5之后 这种模式才能达到正常的效果。

具体参见:http://blog.163.com/lby67224262@126/blog/static/1714153412011121104932660

 

二,你写的单例模式真的能用嘛?

 

下面我们考虑几种会破坏单例的情况,和解决办法

1,多个classloader 获得 不同的单例对象。举个例子,一些servlet容器可能用不同的classloder 去加载servlet,如果servlet引用了同一个单例类就会产生多个实例。

    如果想你的单例类能被同一个类加载器加载,那么就必须自己制定这个类加载器。代码如下

public class Snippet {
	private static Class getClass(String classname)
			throws ClassNotFoundException {
		ClassLoader classLoader = Thread.currentThread()
				.getContextClassLoader();
		if (classLoader == null)
			classLoader = Singleton.class.getClassLoader();
		return (classLoader.loadClass(classname));
	}
}

 

2,你的单例也有可能实现序列化接口,只要是实现了次接口,就有可能被反序列化。没一次反序列化创建对象,就会产生一个新的单例类的实例。解决办法如下。

public class Singleton implements java.io.Serializable {
	public static Singleton INSTANCE = new Singleton();
	protected Singleton() {
		// Exists only to thwart instantiation.
	}
	private Object readResolve() {
		return INSTANCE;
	}
}

 3,反射,还能访问private的构造方法。这样就可以创建很多对象。解决办法如下。

     

      反射时可以使用setAccessible方法来突破private的限制,我们需要做到第一点工作的同时,还需要在在 ReflectPermission("suppressAccessChecks") 权限下使用安全管理器(SecurityManager)的checkPermission方法来限制这种突破。一般来说,不会真的去做这些事情,都是通过应用服务器进行后台配置实现。具体请参考http://blog.csdn.net/yaerfeng/article/details/7103397

 

4,涉及到跨JVM(集群、远程EJB等)时 也会出现产生多个单例类的对象的问题。


      在一个多元 JVM 环境中,每个 JVM 拥有自己对于该单例对象的拷贝,这将导致很多问题,尤其是在对于资源的访问需要受限和加锁的集群的环境中。

      解决方式可以通过应用服务器提供的API 或者第三方的工具。详情参考http://blog.csdn.net/defonds/article/details/12705677

 

 

 

分享到:
评论

相关推荐

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

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

    单例模式案例和笔记,通过案例来了解单例模式

    单例模式是软件设计模式中的一种,其核心思想是确保一个类在整个系统中只有一个实例存在。这在Java中尤其有用,因为多个实例可能导致资源浪费,比如内存占用过多,或者不同实例间的操作不一致。Java中的23种设计模式...

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

    首先,让我们深入了解单例模式。单例模式的主要目的是限制类的实例化,防止无控制的创建多个对象,尤其是当这个类需要频繁地进行全局共享或者资源管理时。在C++中,通常通过私有构造函数和静态成员函数来实现单例。...

    Java 单例模式 工具类

    首先,让我们了解单例模式的几种常见实现方式: 1. 饿汉式(静态常量): 这是最简单的单例实现,它在类加载时就完成了初始化,因此是线程安全的。 ```java public class Singleton { private static final ...

    设计模式 中的 单例模式和观察者模式

    首先,让我们深入了解单例模式。单例模式是一种确保一个类只有一个实例,并提供全局访问点的设计模式。这种模式在资源管理、缓存、对话框、注册表设置、日志记录等场景中非常有用。为了实现单例,通常我们会创建一个...

    Qt qml Singleton 单例模式

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

    OC单例模式讲解

    #### 二、为什么使用单例模式? 1. **节省资源**:通过单例模式可以保证系统内存中该类只存在一个对象,从而避免了多次实例化同一对象所导致的性能开销。 2. **全局唯一性**:单例模式能够确保对象的唯一性,这对于...

    php单例模式实例

    在PHP编程中,单例模式是一种非常常用的设计模式,它保证了一个类只有一个实例,并提供一个全局访问点。这种模式在管理共享资源、数据库连接、缓存处理等场景...了解并熟练掌握单例模式,对于提升PHP开发技能至关重要。

    工厂模式与单例模式

    在软件设计模式中,工厂模式和单例模式是两种非常基础且重要的模式,它们都是用于解决对象创建问题,但有着不同的设计理念和应用场景。本篇文章将深入探讨这两种模式,并结合具体的代码示例`myFactoryDemo`进行讲解...

    单例模式源代码

    单例模式是软件设计模式中的一种经典模式,它在Java编程中被广泛使用。这个模式的主要目的是确保一个类只有一个实例...了解并掌握这些单例模式的实现方式,对于后续学习其他设计模式以及提高代码质量都是非常有帮助的。

    单例模式(用winform写的)

    通过分析这个示例代码,我们可以学习如何将单例模式融入到实际项目中,了解如何创建和使用单例类,以及如何在Winform的环境中处理多线程问题。 单例模式的优点包括: 1. 节省内存:避免了多次实例化同一对象。 2. ...

    Java自定义连接池,单例模式

    在Java编程中,连接池和单例模式是两个非常重要的概念,它们对于高效地管理和使用数据库资源至关重要。这里我们将深入探讨这两个主题,并结合一个通用的BaseDao类来说明如何实现数据库的快速访问。 首先,连接池是...

    IOS单例模式Demo

    单例模式是软件设计模式中的一种,它保证一...了解并熟练运用单例模式,可以帮助你更好地设计和组织iOS应用程序的架构,提高代码的复用性和稳定性。通过分析和研究`Singleton`中的示例,你将对单例模式有更深入的理解。

    单例模式Demo

    单例模式是软件设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。这种模式在资源管理、缓存、线程池等场景中非常常见,因为这些场景往往需要一个唯一的对象来协调系统资源或者执行特定的任务。在...

    ActionScript 3.0里使用单例模式

    通过本文的介绍,我们详细了解了在ActionScript 3.0中如何正确地实现单例模式。单例模式不仅有助于提高程序的可维护性和可扩展性,还能有效优化资源管理。在实际开发中合理运用单例模式,能够显著提升项目的质量和...

    JAVA单例模式的登录案例

    根据给定的信息,我们可以深入探讨Java...综上所述,通过本案例的学习,我们不仅了解了如何在Java中实现单例模式,还掌握了基于Swing框架开发简单图形界面的基本方法,这对于后续学习Java GUI编程具有很好的参考价值。

    适合初学者的音频播放器代码C#单例模式设计

    在这个项目中,“适合初学者的音频播放器代码C#单例模式设计”为学习者提供了一个很好的起点,它通过实际的应用场景来讲解单例模式。 单例模式是设计模式的一种,它的核心思想是确保一个类只有一个实例,并提供全局...

    多线程单例模式并发访问

    在讨论多线程单例模式及并发访问之前,我们先来了解一些基本概念。 **进程**和**线程**是计算机科学中的两个核心概念,它们之间的关系紧密而复杂。 - **进程**:具有独立功能的程序在某个数据集合上的一次运行活动...

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

    首先,我们需要了解Java中的单例模式实现方式。常见的有懒汉式、饿汉式、双重检查锁定(DCL)以及静态内部类四种。其中,DCL和静态内部类是最推荐的,因为它们既实现了线程安全,又保证了单例的唯一性,并且延迟加载...

Global site tag (gtag.js) - Google Analytics