`
flyPig
  • 浏览: 139855 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Java中的Singleton pattern

阅读更多
一般的单例模式
public class Singleton { 
   private static Singleton instance = null; 
  
   private Singleton() {  
   } 

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

此方式已知问题:
1.如果由不同的ClassLoader去load,有可能存在多个实例。
2.线程不安全。假设线程1进入if判断,正在创建对象的时候,线程2进入,判断成功,这样就会创建2个对象实例。

改进方式,同步化getInstance方法,也就是懒汉式写法。
public class Singleton { 
   private static Singleton instance = null; 
  
   private Singleton() {  
   } 

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

但getInstance会被外部线程比较频繁的调用,同步比非同步的性能消耗要昂贵很多,因此这样的做法会存在很大的额外性能消耗。因此产生另外一种改进方式,双重检查写法。
public static Singleton getInstance() { 
  if(singleton == null) { 
     synchronized(Singleton.class) { 
       if(singleton == null) { 
         singleton = new Singleton(); 
       } 
    } 
  } 
  return singleton; 
} 

此写法存在的问题在于singleton = new Singleton() 这句代码对于编译器来说,是分两步进行。首先初始化一个singleton对象并随意赋一个值,然后调用Singleton类的构造器赋值。因此在第一步完成后singleton == null这句判断已经不成立了,但此时singleton只是一个临时值。如果线程2此时进入getInstance,就会把这个singleton给返回出去。由于java的内存模型,双重检查并不能成功的起到作用。


采用饿汉式的写法也可避免线程安全问题.但是任何对Singleton类的访问(内部的static final变量除外,因为jvm会把它们直接编译为常量),比如另外一个static方法被访问,会引起jvm去初始化instance,而此时我们的本意是不想加载单例类的。同时因为没有延迟加载,最明显的缺点就是如果构造器内的方法比较耗时,则加载过程会比较长。对于一般的应用,构造方法内的代码不涉及到读取配置、远程调用、初始化IOC容器等长时间执行的情况,用这种方式是最简单的。
public class Singleton { 
   private static Singleton instance = new Singleton(); 
  
   private Singleton() {  
   } 

   public static Singleton getInstance() { 
      return instance; 
   } 
} 


如果我们既想使用延迟加载的好处,让类在被使用的时候才去加载,又想避免额外的同步调用开销,同时还不使用双重检查的模式,可以用初始化一个中间的容器类来解决这个问题。
就可以用如下的写法:
public class Singleton {  
   private static class SingletonHolder {   
      static Singleton instance = new Singleton();       
   }   
   
   public static Singleton getInstance() {   
       return SingletonHolder.instance;   
   } 

   private Singleton() {  } 

} 

java中,只有一个类被用到的时候才被初始化。在getInstance方法被调用的时候,如果SingletonHolder类没有被加载,就会去加载,起到延迟加载的作用,同时也能保持多线程下的语义正确性。

如果是jdk1.5以上,还可采用enum方式来实现,也可避免多线程的问题。
enum Singleton {   
  INSTANCE;   
  public static Singleton getInstance() {   
    return INSTANCE;   
  } 

  public void sss() {
    System.out.println("sss");
  }
}


关于反射
以上的写法,除了枚举方式,其他的写法均可用反射得到Constructor来newInstance得到实例。

关于继承
无论是饿汉式,懒汉式写法均不可继承,因此有如下登记式写法。
public class Sington {
	private static HashMap<String, Sington> map = new HashMap<String, Sington>();

	protected Sington() {
	}

	public static synchronized Sington getInstance(String classname) {
		Sington singleton = (Sington) map.get(classname);
		if (singleton != null) {
			return singleton;
		}
		try {
			singleton = (Sington) Class.forName(classname).newInstance();
		} catch (ClassNotFoundException cnf) {

		} catch (InstantiationException ie) {

		} catch (IllegalAccessException ia) {

		}
		map.put(classname, singleton);
		return singleton;
	}
}

对于子类的
public class SingtonChild extends Sington {
	public SingtonChild() {
	}

	static public SingtonChild getInstance() {
		return (SingtonChild) Sington
				.getInstance("util.SingtonChild");
	}

	public String about() {
		return "Hello,I am children.";
	}
}


ClassLoader对单例的影响
同样的单例类,由不同的ClassLoader装载就会有多个实例,为了确保我们的单例类只会被同个类ClassLoader加载,我们就需要为它指定一个ClassLoader
private static Class getClass(String classname)  
                                         throws ClassNotFoundException { 
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();  
      if(classLoader == null) 
         classLoader = Singleton.class.getClassLoader();  
      return (classLoader.loadClass(classname)); 
   } 
} 


序列化对单例的影响
如果单例类实现了序列化接口,我们序列化一次,然后反序列化多次,会得到多个不同实例。我们通过实现readResolve()来解决这个问题。
public class Singleton implements Serializable { 
   private static Singleton instance = null; 
  
   private Singleton() {  } 

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

   private Object readResolve() { 
        return instance; 
   }
 } 


JavaAPI中有哪些用到了单例模式
Runtime类
Runtime.getRuntime();

它里面用到的是饿汉式写法。
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() { 
	return currentRuntime;
    }


Note:网上查到不少文章说Calendar类是singleton模式的应用,进入JDK1.5的sourcecode,发现并不是这样,纯粹是谬误。
很简单的代码就可以验证:
	Calendar c1 = Calendar.getInstance();
		Calendar c2 = Calendar.getInstance();
		Runtime runtime1 = Runtime.getRuntime();
		Runtime runtime2 = Runtime.getRuntime();
		System.out.println(c1 == c2);
		System.out.println(runtime1 == runtime2);

分享到:
评论

相关推荐

    singleton pattern

    singleton pattern 的定义 主要应用方法 优缺点 通过代码 具体分析解释

    Singleton pattern单例模式应用

    单例模式(Singleton Pattern)是一种常用的软件设计模式,在系统中确保某个类只有一个实例,并提供一个全局访问点。这种模式通常用于控制资源的消耗,比如数据库连接、线程池等,以及确保某个系统配置的一致性。 #...

    Singleton Pattern 源码

    单例模式(Singleton Pattern)是软件设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。这种模式在很多场景下非常有用,比如控制资源的唯一性、全局配置对象或者缓存服务等。本篇文章将深入探讨...

    单例模式 Singleton Pattern

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

    Android SingletonPatternDemo

    Java据说有23种设计模式,Android的设计模式肯定是由Java来引申出来的。这里不讨论有多少人全会,有多少种设计模式会使用到,我们来讲下其中用得最多的也就是人人都知道的...这里是一个简单的SingletonPatternDemo。

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

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

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

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

    java-thread-safe-singleton-pattern.rar_starvation

    在Java编程中,单例模式是一种常用的创建型设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。本主题探讨的是线程安全的单例模式实现,特别是饥饿模式(Starvation)和延迟加载(Lazy Initialization)...

    JAVA software design pattern

    Java软件设计模式是一种在开发过程中遵循的成熟、可重用的解决方案模板,它解决了软件设计中的常见问题。设计模式并不具体实现任何代码,而是提供了一种通用的语言,帮助开发者理解和交流复杂的系统架构。以下是关于...

    java pattern

    jar cvf singleton.jar ./ (当前目录打包,D:\\workspace\\java\\src\\com\\javaeye\\lindows\\design23) 博文链接:https://lindows.iteye.com/blog/221957

    java设计模式详解,java design pattern

    1. 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。单例模式的实现有多种方式,如饿汉式初始化、静态块初始化、懒汉式初始化、线程安全的单例、Bill Pugh实现方式和枚举实现方式。例如...

    设计模式_单例模式.zip

    单例模式是软件设计中的...在Java中,我们可以使用多种方式来实现单例,包括传统的同步方法、双重检查锁定和枚举。然而,使用单例模式时,也需要考虑其可能带来的问题,如测试困难、违背开闭原则等,以便做出最佳选择。

    JAVA design pattern-java设计模式

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

    单例模式Singleton

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

    designPattern-java.pdf

    ### 设计模式在Java中的应用 #### 设计模式概述与背景 设计模式是软件工程领域的一个重要概念,它提供了一套解决常见问题的有效方案。这些模式不仅能够帮助开发者编写更高质量、可维护性更强的代码,还能促进团队...

    Professional.Java.EE.Design.Pattern

    - 单例模式(Singleton Pattern):保证一个类只有一个实例,并提供一个全局访问点。在Java EE中,单例模式常用于管理状态信息或共享资源。 - 依赖注入(Dependency Injection)和CDI(Contexts and Dependency ...

    Design Pattern In Java.pdf

    - 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。书中讨论了单例模式的实现方式,如通过异常抛出、静态类和静态方法创建单例,以及在大型程序中查找单例的挑战和影响。 - 建造者...

    单例模式(Singleton Pattern)

    单例模式是设计模式中的一种,它用于控制类的实例化过程,确保在整个应用程序中,对于特定类,只有一个实例存在。这种模式常被用来管理共享资源或者全局配置,以提高性能和减少系统间的耦合。 单例模式的核心在于...

Global site tag (gtag.js) - Google Analytics