java面试(1)单例模式的几种写法

面试中单例模式大体可分为4类,下面分别介绍他们的基本形式、变种及特点。

一、饱汉模式

1.0基础版

/**
 * 饱汉模式--线程不安全
 * 基础的饱汉-饱汉,即已经吃饱,不着急再吃,饿的时候再吃。所以他就先不初始化单例,等第一次使用的时候再初始化,即“懒加载”。
 * 饱汉模式的核心就是懒加载。
 * 好处是更启动速度快、节省资源,一直到实例被第一次访问,才需要初始化单例;小坏处是写起来麻烦,大坏处是线程不安全,if语句存在竞态条件。
 * @author zengdq
 * @date 2017-11-22
 */
public class Singleton1 {
	/** 静态私有成员变量instance*/
	 private static Singleton1 instance = null;
	 
	/** 私有构造函数*/
	 private Singleton1(){
		 //空的构造函数
	 }  
	
	/** 静态公有方法Instance()*/
	public static Singleton1 getInstance() {
		if(instance == null){//懒汉式
			instance = new Singleton1();
		}
		return instance;
	}
	 
	 
}

1.1 变种1版

/**
 * 饱汉变种 1--线程安全
 * 最粗暴的犯法是用synchronized关键字修饰getInstance()方法,这样能达到绝对的线程安全。
 * 变种1的好处是写起来简单,且绝对线程安全;坏处是并发性能极差,事实上完全退化到了串行。
 * 单例只需要初始化一次,但就算初始化以后,synchronized的锁也无法避开,从而getInstance()完全变成了串行操作。性能不敏感的场景建议使用。
 * @author zengdq
 * @date 2017-11-22
 */
public class Singleton1_1 {
	/** 静态私有成员变量instance*/
	 private static Singleton1_1 instance = null;
	 
	/** 私有构造函数*/
	 private Singleton1_1(){
		 //空的构造函数
	 }  
	
	/** 静态公有方法Instance()*/
	public synchronized static Singleton1_1 getInstance() {
		if(instance == null){//懒汉式
			instance = new Singleton1_1();
		}
		return instance;
	}
	 
	 
}

1.2 变种2版

/**
 * 饱汉变种 2--线程不安全
 * 变种2是“臭名昭著”的DCL 1.0。
 * 针对变种1中单例初始化后锁仍然无法避开的问题,变种2在变种1的外层又套了一层check,加上synchronized内层的check,即所谓“双重检查锁”(Double Check Lock,简称DCL)。
 * 变种2的核心是DCL,看起来变种2似乎已经达到了理想的效果:懒加载+线程安全。
 * 可惜的是,正如注释中所说,DCL仍然是线程不安全的,由于指令重排序,你可能会得到“半个对象”。
 * @author zengdq
 * @date 2017-11-22
 */
public class Singleton1_2 {
	/** 静态私有成员变量instance*/
	 private static Singleton1_2 instance = null;
	 
	/** 私有构造函数*/
	 private Singleton1_2(){
		 //空的构造函数
	 }  
	
	/** 静态公有方法Instance()*/
	public synchronized static Singleton1_2 getInstance() {
		if(instance == null){//懒汉式
			synchronized (Singleton1_2.class) {
				if(instance == null){
					instance = new Singleton1_2();
				}
			}
		}
		return instance;
	}
	 
}

1.3 变种3版

/**
 * 饱汉变种3--线程安全
 * 变种3专门针对变种2,可谓DCL 2.0。
 * 针对变种3的“半个对象”问题,变种3在instance上增加了volatile关键字。
 * 多线程环境下,变种3更适用于性能敏感的场景。
 * @author zengdq
 * @date 2017-11-22
 */
public class Singleton1_3 {
	/** 静态私有成员变量instance*/
	 private static volatile Singleton1_3 instance = null;
	 
	/** 私有构造函数*/
	 private Singleton1_3(){
		 //空的构造函数
	 }  
	
	/** 静态公有方法Instance()*/
	public synchronized static Singleton1_3 getInstance() {
		if(instance == null){//懒汉式
			synchronized (Singleton1_3.class) {
				if(instance == null){
					instance = new Singleton1_3();
				}
			}
		}
		return instance;
	}
	 
}


二、饿汉模式

/**
 * 饿汉模式--线程安全
 * 与饱汉相对,饿汉很饿,只想着尽早吃到。所以他就在最早的时机,即类加载时初始化单例,以后访问时直接返回即可。
 * 饿汉的好处是天生的线程安全(得益于类加载机制),写起来超级简单,使用时没有延迟;坏处是有可能造成资源浪费(如果类加载后就一直不使用单例的话)。
 * 值得注意的时,单线程环境下,饿汉与饱汉在性能上没什么差别;但多线程环境下,由于饱汉需要加锁,饿汉的性能反而更优。
 * @author zengdq
 * @date 2017-11-22
 */
public class Singleton2 {
	/** 静态私有成员变量instance*/
	 private static Singleton2 instance = new Singleton2();
	 
	/** 私有构造函数*/
	 private Singleton2(){
		 //空的构造函数
	 }  
	
	/** 静态公有方法Instance()*/
	public static Singleton2 getInstance() {
		return instance;
	}
	 
}

三、Holder模式

/**
 * Holder模式--线程安全
 * 我们既希望利用饿汉模式中静态变量的方便和线程安全;又希望通过懒加载规避资源浪费。
 * Holder模式满足了这两点要求:核心仍然是静态变量,足够方便和线程安全;通过静态的Holder类持有真正实例,间接实现了懒加载。
 * 相对于饿汉模式,Holder模式仅增加了一个静态内部类的成本,与饱汉的变种3效果相当(略优),都是比较受欢迎的实现方式。同样建议考虑。
 * @author zengdq
 * @date 2017-11-22
 */
public class Singleton3 {
	/** 内部类*/
	 private static class SingletonHolder{
		 /** 静态私有成员变量instance*/
		 private static final Singleton3 instance= new Singleton3();
		 
		 /** 私有构造函数*/
		 private SingletonHolder(){
			//空的构造函数
		 }
	 };
	 
	/** 私有构造函数*/
	 private Singleton3(){
		 //空的构造函数
	 }  
	
	/** 静态公有方法Instance()*/
	public static Singleton3 getInstance() {
		return SingletonHolder.instance;
	}
	 
	 
}

四、枚举模式

/**
 * 枚举模式--线程安全
 * 将枚举的静态成员变量作为单例的实例
 * 代码量比饿汉模式更少。
 * 但用户只能直接访问实例Singleton4.SINGLETON——事实上,这样的访问方式作为单例使用也是恰当的,只是牺牲了静态工厂方法的优点,如无法实现懒加载
 * @author zengdq
 * @date 2017-11-22
 */
public enum Singleton4 {
	SINGLETON
}

小结:

上面的分析都忽略了反射和序列化的问题。通过反射或序列化,我们仍然能够访问到私有构造器,
创建新的实例破坏单例模式。 此时,只有枚举模式能天然防范这一问题。

下面继续忽略反射和序列化的问题,做个总结回味一下:

猜你喜欢

转载自blog.csdn.net/zengdeqing2012/article/details/78604410