`
jackroomage
  • 浏览: 1214941 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

单例模式归纳总结

阅读更多

一般的单例模式

Java代码 复制代码
  1. public class Singleton {    
  2.    private static Singleton instance = null;    
  3.      
  4.    private Singleton() {     
  5.    }    
  6.   
  7.    public static Singleton getInstance() {    
  8.       if(instance == null) {    
  9.          instance = new Singleton();    
  10.       }    
  11.       return instance;    
  12.    }    
  13. }   
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方法,也就是懒汉式写法。

Java代码 复制代码
  1. public class Singleton {    
  2.    private static Singleton instance = null;    
  3.      
  4.    private Singleton() {     
  5.    }    
  6.   
  7.    public static synchronized Singleton getInstance() {    
  8.       if(instance == null) {    
  9.          instance = new Singleton();    
  10.       }    
  11.       return instance;    
  12.    }    
  13. }  
public class Singleton { 
   private static Singleton instance = null; 
  
   private Singleton() {  
   } 

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


但getInstance会被外部线程比较频繁的调用,同步比非同步的性能消耗要昂贵很多,因此这样的做法会存在很大的额外性能消耗。因此产生另外一种改进方式,双重检查写法。

Java代码 复制代码
  1. public static Singleton getInstance() {    
  2.   if(singleton == null) {    
  3.      synchronized(Singleton.class) {    
  4.        if(singleton == null) {    
  5.          singleton = new Singleton();    
  6.        }    
  7.     }    
  8.   }    
  9.   return singleton;    
  10. }   
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容器等长时间执行的情况,用这种方式是最简单的。

Java代码 复制代码
  1. public class Singleton {    
  2.    private static Singleton instance = new Singleton();    
  3.      
  4.    private Singleton() {     
  5.    }    
  6.   
  7.    public static Singleton getInstance() {    
  8.       return instance;    
  9.    }    
  10. }   
public class Singleton { 
   private static Singleton instance = new Singleton(); 
  
   private Singleton() {  
   } 

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



如果我们既想使用延迟加载的好处,让类在被使用的时候才去加载,又想避免额外的同步调用开销,同时还不使用双重检查的模式,可以用初始化一个中间的容器类来解决这个问题。
就可以用如下的写法:

Java代码 复制代码
  1. public class Singleton {     
  2.    private static class SingletonHolder {      
  3.       static Singleton instance = new Singleton();          
  4.    }      
  5.       
  6.    public static Singleton getInstance() {      
  7.        return SingletonHolder.instance;      
  8.    }    
  9.   
  10.    private Singleton() {  }    
  11.   
  12. }   
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方式来实现,也可避免多线程的问题。

Java代码 复制代码
  1. enum Singleton {      
  2.   INSTANCE;      
  3.   public static Singleton getInstance() {      
  4.     return INSTANCE;      
  5.   }    
  6.   
  7.   public void sss() {   
  8.     System.out.println("sss");   
  9.   }   
  10. }  
enum Singleton {   
  INSTANCE;   
  public static Singleton getInstance() {   
    return INSTANCE;   
  } 

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



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

关于继承
无论是饿汉式,懒汉式写法均不可继承,因此有如下登记式写法。

Java代码 复制代码
  1. public class Sington {   
  2.     private static HashMap<String, Sington> map = new HashMap<String, Sington>();   
  3.   
  4.     protected Sington() {   
  5.     }   
  6.   
  7.     public static synchronized Sington getInstance(String classname) {   
  8.         Sington singleton = (Sington) map.get(classname);   
  9.         if (singleton != null) {   
  10.             return singleton;   
  11.         }   
  12.         try {   
  13.             singleton = (Sington) Class.forName(classname).newInstance();   
  14.         } catch (ClassNotFoundException cnf) {   
  15.   
  16.         } catch (InstantiationException ie) {   
  17.   
  18.         } catch (IllegalAccessException ia) {   
  19.   
  20.         }   
  21.         map.put(classname, singleton);   
  22.         return singleton;   
  23.     }   
  24. }  
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;
	}
}


对于子类的

Java代码 复制代码
  1. public class SingtonChild extends Sington {   
  2.     public SingtonChild() {   
  3.     }   
  4.   
  5.     static public SingtonChild getInstance() {   
  6.         return (SingtonChild) Sington   
  7.                 .getInstance("util.SingtonChild");   
  8.     }   
  9.   
  10.     public String about() {   
  11.         return "Hello,I am children.";   
  12.     }   
  13. }  
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

Java代码 复制代码
  1. private static Class getClass(String classname)     
  2.                                          throws ClassNotFoundException {    
  3.       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     
  4.       if(classLoader == null)    
  5.          classLoader = Singleton.class.getClassLoader();     
  6.       return (classLoader.loadClass(classname));    
  7.    }    
  8. }   
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()来解决这个问题。

Java代码 复制代码
  1. public class Singleton implements Serializable {    
  2.    private static Singleton instance = null;    
  3.      
  4.    private Singleton() {  }    
  5.   
  6.    public static synchronized Singleton getInstance() {    
  7.       if(instance == null) {    
  8.          instance = new Singleton();    
  9.       }    
  10.       return instance;    
  11.    }    
  12.   
  13.    private Object readResolve() {    
  14.         return instance;    
  15.    }   
  16.  }   
分享到:
评论

相关推荐

    C# 设计模式之单例模式归纳总结

    "C# 设计模式之单例模式归纳总结" C# 设计模式之单例模式是一种常用的设计模式,它限制了类的实例化,使得某个类只能有一个实例,并提供了一个全局访问点来访问该实例。本文将对单例模式的定义、优缺点、实现要点和...

    JAVA设计模式-原则和23种设计模式归纳总结

    JAVA设计模式-原则和23种设计模式归纳总结 本资源主要介绍了JAVA设计模式的原则和23种设计模式的总结。设计模式是软件设计中的一种解决方案,能够使软件系统更加灵活、可维护和可扩展。本资源首先介绍了设计模式的...

    C#单例模式(Singleton Pattern)详解

    (新手写博客,主要是对自己学习的归纳总结。会对很多小细节详解。) 单例模式的定义: 确保一个类只有一个实例,并提供一个全局访问点。 首先实例大家应该都明白就是类生成对象的过程简单的就是String s=new String...

    java单例设计模式-饿汉式-懒汉式[归纳].pdf

    单例模式常用于管理共享资源,如配置信息的读取,以简化系统协调和避免资源浪费。 单例模式的两个主要特点: 1. 限制类的实例化,仅允许存在一个实例。 2. 提供一个公共的访问点,使得其他对象可以获取这个唯一的...

    免费开源!!Java 23种设计模式全归纳

    创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。 结构类型模式:队列模式、桥接模式、装饰模式、组合模式、外观模式、共享元模式、代理模式。 行为类型模式:模版方法模式、命令模式、迭代器...

    大话设计模式总结.docx

    本文档对设计模式进行了总结和归纳,主要包括设计模式的原则和具体设计模式的特点。 设计模式的原则是指在设计模式中需要遵循的一些基本规则,它们是指导设计模式的实施和应用。常见的设计模式原则包括: 1. 开放-...

    零设计模式:设计模式总结归纳

    ),观察者模式,单例模式,组合模式(树结构),装饰模式(任意重叠新功能) 关注个人微信公众号JavaStorm为每个设计模式都有详细解释。 com.zero.design包下包含了23种设计模式 com.zero.headfirst是抽出常用的...

    设计模式代码和报告

    2. 使学生了解23种设计模式,归纳总结创建型模式、行为型模式和结构型模式的应用情景、所需角色。并根据分类各举一例详细说明要析 3. 使学生掌握如何使用面向对象的方法,采用设计模式设计可扩展、高可重用的软件。 ...

    Java基础知识点总结.docx

    设计模式是解决常见软件设计问题的经验总结,如单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式、适配器模式、桥接模式、过滤器模式、组合模式、装饰器模式、外观模式、享元模式、代理模式、责任链模式、...

    Java单利模式与多线程总结归纳

    Java中的单例模式是一种常用的软件设计模式,它的主要目的是确保一个类只有一个实例,并提供全局访问点。单例模式有三个关键特性:唯一性、自我创建和全局访问。在Java中,单例模式通常通过以下三种方式实现: 1. *...

    CC++与设计模式基础课程-讲义

    创建型模式关心对象的创建过程,包括工厂方法模式、抽象工厂模式、建造者模式、原型模式和单例模式等。结构型模式关心类和对象的组合,包括代理模式、装饰者模式、适配器模式、桥接模式、组合模式、外观模式和享元...

    前端面试知识点总结与归纳

    - 单例模式确保类只有一个实例,常用于全局配置和消息组件。 - 工厂模式根据参数创建不同类型的对象,适应多态需求。 - 工厂方法模式让子类决定实例化哪个类,实现创建逻辑的抽象。 - 抽象工厂模式创建对象家族,...

    GOF设计模式(中英文双语)

    1. **创建型模式**:这类模式主要关注对象的创建过程,包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。例如,单例模式确保一个类只有一个实例,并提供全局访问点;工厂方法模式将对象的创建过程...

    Java 23种设计模式全归纳

    1. 单例模式(Singleton):确保一个类只有一个实例,并提供全局访问点。在Java中,可以使用双重检查锁定(Double-Checked Locking)、静态内部类或枚举来实现单例。 2. 工厂模式(Factory):提供创建对象的接口,...

    java设计模式

    在Java中,设计模式的应用可以分为三大类:创建型模式(如工厂模式、抽象工厂模式、单例模式、建造者模式和原型模式)、结构型模式(如适配器模式、装饰器模式、代理模式、桥接模式、组合模式、外观模式和享元模式)...

    C++设计模式基础教程.pdf

    C++设计模式基础教程.pdf 本资源摘要信息涵盖了C++设计模式基础...学习设计模式需要积累案例,大量背类图,多思考、多梳理、归纳总结,尊重事物的认知规律,注意事物临界点的突破。不可急躁,需要不断的追求和实践。

    设计模式面试题.pdf

    **设计模式**是一种在软件工程领域广泛应用的概念,它是指一系列被广泛接受、经过验证的问题解决方案的归纳与总结。通过使用设计模式,开发者能够编写出更易于理解和维护的代码,同时也提高了代码的可靠性和重用性。...

    CC++与设计模式讲义.pdf

    此外,文件中也提到学习设计模式的方法,比如对于初学者来说,积累案例比背诵类图更为重要,对于开发者,建议多思考和归纳总结,并且在学习设计模式时不应浮躁,需要在适当的开发环境中寻找合适的设计模式去解决问题...

Global site tag (gtag.js) - Google Analytics