设计模式->单例模式

单例模式是设计模式中最常见的一个设计模式,属于创建类模式,它保证一个类在在程序运行过程中只有一个实例化对象,既节省了资源,同时又保护了系统临界资源。比如操作系统对外设的管理,打印机只有一个,一次在任何时刻只能有一个打印机的实例。

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

接着,我们以打印机为例通过代码来展示不同的单例实现方式。

1. 懒汉式

所谓懒汉式,这涉及到了一个常见的名词“懒加载”。所谓懒加载,就是指在一个类的实例就只有在使用的时候才会创建,而不用的时候并不会去加载。通俗来说,就是它太懒了。这样的一个好处是避免了资源的占用,相应地提高了资源的利用率。代码如下:

public class Printer{

	private static Printer instance;

	private Printer(){}

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

但是上面的代码存在一个问题,那就是不支持多线程。试想,同时多个线程懒加载同时调用getInstance方法,那么会产生三个Printer对象,没有真正实现单例模式。为了弥补这个问题,我们通过给getInstance方法加上synchronized关键字来支持多线程。

2. 饿汉式

 所谓饿汉式,通俗点讲就是指它太饿了,所以它在一开始的时候就已经实例化,区别于懒汉式的是给对象实例化的时间不同。这个方式是最常用的单例模式,但是缺点是由于一开始就实例化了对象,造成了资源的浪费。代码实现如下:

public class Printer{

	private static Printer instance = new Printer();

	private Printer(){}

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

3. 双检锁

 通过对比之前的两个实现方式,明显发现采用了懒加载的懒汉式的资源利用效率高,但是,懒汉式的效率并不是如我们想象的那样高。每次当有线程去调用getInstance方法时,都会由于synchronized关键字的缘故而不得不等待正在访问该方法的进程。而事实上这是不必要的。假设该类已经实例化,线程A和线程B来调用getInstance方法,由于并不需要初始化,所以synchronized关键字在此时拉低了程序执行执行的效率,由此我们提出了双检锁的方法来实现。代码如下:

public class Printer{

	private static Printer instance ;

	private Printer(){}

	public static Printer getInstance(){
		if (instance == null) {
			synchronized(printer.class){
				if (instance == null) {
					instance = new Printer();
				}
			}
		}
		return instance;
	}
}

但是,此时的代码还是有问题的。 由于本片博文是用来介绍单例模式的,详细的优化就不介绍了,感兴趣的参考这篇博文,里面详细的讲解了双检锁法的处理优化。

https://www.cnblogs.com/dquery/p/7077154.html

4. 登记式

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。代码如下:

public class Printer{

	private static class PrinterHolder{
		private static Printer Instance = new Printer();
	}

	private Printer(){}

	public static final Printer getInstance(){
		return PrinterHolder.Instance;
	}
}

5. 枚举

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。不能通过 reflection attack 来调用私有构造方法。 

public enum Printer {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值