单例模式的定义


单例模式确保类只有一个实例,并且提供一个全局的访问点。


懒汉式单例模式:延迟实例化,但节省空间


package com.sxh.singleton;
public class SingleTon {
	/*
	 * volatile关键字确保:当uniqueInstance变量被初始化为SingleTon实例时,多个线程能正确的处理uniqueInstance变量
	 * 分析:volatile修饰的成员变量,在每次被线程访问时,都强制性的从共享内存重读该成员的值;
	 * 当值发生变化是,强制线程将变化值写入共享内存,任何时候不同线程总是看到你某个成员变量的同一个值
	 * */
	private volatile static SingleTon uniqueInstance;//利用一个静态变量来记录SingleTon类的唯一实例
	//其他有用的单件类的数据
	
	
	private SingleTon(){} //类外无法访问
	public  static SingleTon getInstance(){ 
		/*
		 * 使用”双重检查加锁“,在getInstance中减少使用同步
		 * 首先检查是否实例已经创建了,如果尚未创建,才进行同步;只有第一次访问getInstance会同步
		*/
		if(uniqueInstance==null){  //确保只有一个实例
			synchronized (SingleTon.class) { //多线程的情况不会出现问题,线程同步问题
				if(uniqueInstance==null){
					uniqueInstance=new SingleTon();//如果我们不需要这个实例,则永远不会产生
				}
			}			
		}
		return uniqueInstance;
	}
	//其他有用的单件类的方法,单件类也可以是一般的类,具有一般的数据和方法
	
	
}



分析:在需要的情况下,才创建唯一的实例对象,是一种延迟实例化的方法。但是要考虑线程同步的问题,会降低执行效率,是一个以时间换空间的方法。



饿汉式单例模式:急切的创建实例,而不用延迟实例化


代码如下:



package com.sxh.singleton;
public class SingleTon {
	
	private volatile static SingleTon uniqueInstance=new SingleTon();
	//其他有用的单件类的数据	
	
	private SingleTon(){} //类外无法访问
	public  static SingleTon getInstance(){ 
		return uniqueInstance;
	}
	//其他有用的单件类的方法,单件类也可以是一般的类,具有一般的数据和方法
		
}


分析:对静态变量初始化,JVM在类加载时就马上创建唯一的单例对象;


不用考虑线程见同步的问题,但可能会浪费空间,是一种以空间换时间的方法。

IoDH实现单例模式



实现IoDH时,需要在单例类中增加一个静态内部类,在该内部类中创建单例对象,再将该单例对象通过getInstance()方法给外部使用。



代码示例如下:



package com.sxh.singleton;
public class SingleTon {
	
	private SingleTon(){} //类外无法访问
	private static class HolderClass{ //静态内部类
		private static final SingleTon uniqueinstance=new SingleTon(); 
	}
	public  static SingleTon getInstance(){ 
		return HolderClass.uniqueinstance;
	}
	//其他有用的单件类的方法,单件类也可以是一般的类,具有一般的数据和方法
		
}



分析:由于静态单例对象没有作为Singlton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()方法时将加载内部类HolderClass,在该内部类中定义一个static类型的静态变量,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。



通过使用IoDH,既可以实现延迟加载,又可以保证线程安全,不影响系统性能,因此,IoDH不失为一种最好的Java语言单例模式实现方式。



单例模式的背景需求:



 在使用注册表对象、日志对象等情况下,这类对象只能有一个实例;如果有多个实例,可能会导致很多问题产生,例如:程序的行为异常、资源使用过量、或者不一致的结果。



单例模式和全局变量的比较:



1. 首先可以确保只有一个实例被创建,单例模式也给我们一个全局的访问点,和全局变量一样方便。



2.全局变量和静态变量,在编译时就分配了空间并且初始化;如果将对象赋值给一个全局变量,必须在程序的开始就创建对象;如果对象非常耗费资源,而程序在这次执行过程中又一直没有用到它,就会形成浪费。而使用单例模式,我们可以在需要它的时候才创建对象(延迟实例化)。