面试中的单例模式

最近的两次面试中,都被要求在纸上写代码实现单例(Singleton)模式。下文展示了三种不同的Singleton实现方式:


1.不好的解法一:只适用于单线程环境

public class Singleton1 {

	private static Singleton1 instance = null;

	private Singleton1() {

	}

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

}
这个解法只能适用于单线程的环境,在多线程环境中,可能会出现重复创建instance的情况


2.不好的解法二:虽然在多线程环境中能工作但效率不高
为了保证在多线程环境中,每次只能有一个线程访问getInstance()方法,我们用synchronized关键字修饰这个方法:

public class Singleton2 {

	private static Singleton2 instance = null;

	private Singleton2() {

	}

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

}
这样虽然能够在多线程环境下保证单例,但是Singleton2还不是十分完美,每次通过getInstance获取实例的时候,都会试图加上一个同步锁,而加锁是一个非常耗时的操作,在没有必要的情况下我们应该尽量避免。


3.可行的解法:缩小同步锁范围

我们只是需要在实例还没有创建之前需要加锁操作,以保证只有一个线程创建出实例。而当实例已经创建之后,我们已经不需要再做加锁操作了。

这里我们使用Java中的Lock对象,并进行双重校验:

public class Singleton3 {

	private static Singleton3 instance = null;
	private static ReentrantLock lock = new ReentrantLock(false); // 创建可重入锁,false代表非公平锁

	private Singleton3() {

	}

	public static Singleton3 getInstance() {
		if (instance == null) {
			lock.lock();
			try {
				if (instance == null) {
					instance = new Singleton3();
				}
			} finally {
				lock.unlock();
			}
		}
		return instance;
	}

}

4.可行的解法:类装载时初始化实例

public class Singleton4 {

	private static Singleton4 instance = new Singleton4();

	private Singleton4() {

	}

	public synchronized static Singleton4 getInstance() {
		return instance;
	}

}
这个方法是在类装载时就初始化instance,虽然避免了多线程同步问题,但是没有达到lazy loading的效果

5.推荐方法1:静态内部类

public class Singleton5 {

	private Singleton5() {

	}

	private static class SingletonHolder {
		private static final Singleton5 INSTANCE = new Singleton5();
	}

	public static Singleton5 getInstance() {
		return SingletonHolder.INSTANCE;
	}

}
这种方法也能保证线程安全,而且也达到了lazy loading的效果


6.推荐方法2:枚举

public enum Singleton6 {
	
	INSTANCE;
	
	public void whateverMethod() {

	}

}
这种方式是《Effective Java》作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象


总结:
一般可以使用第4种和第5中方法。如果instance是个重量级的类,实例化时需要消耗很多资源,那么这个时候就考虑第5中方法;

如果涉及反序列化创建对象时,我们考虑第6种枚举方法。


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值