Java设计模式——单例模式

单例对象(Singleton)是一种常用的设计模式,在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在,这样模式的好处在于:

1、某些类的创建比较频繁,对于一些大型的对象,这样的开销是十分巨大的;

2、省去了new操作符,降低了系统内存的使用频率,减轻了garbage collection的压力;

3、有些情况下加入没有使用单例模式,会造成十分严重的后果,例如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了,假如一个军队出现多个司令员,肯定会号令不一,发生乱子。这时候只有使用单例模式,才能保证核心交易引擎服务器独立控制整个流程。

下面创建一个简单的实例对象:

package com.design;

public class Singleton {
	private static Singleton instance = null;//静态实例变量,防止被引用,此处赋值为null,实现延迟加载

	private Singleton() { //防止被实例化

	}

	public static Singleton getInstance() { //静态方法创建实例
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}

	public Object readResolve() {  
		return instance;
	}
}

细心的大家可能会发现,这个类没有丝毫线程安全保护机制,在多线程情况下运行,肯定会出问题,

下面改变实例化方法:

public static synchronized Singleton getInstance() { //静态方法创建实例
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
使用synchronized关键字锁住的是这个对象,这样锁住了这个对象,就会导致程序的性能出现了下降,因为每次调用这个方法都要使用锁机制,但是实际情况下我们只要在第一次创建对象的时候加锁,之后就不需要了,下面改进方法如下:

public static Singleton getInstance() { // 静态方法创建实例

		if (instance == null) {
			synchronized (instance) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
上面的改进解决了上面提到的问题,将synchronized关键字加在了内部,在调用的时候是不需要加锁的,这样就在性能上有了一定的提升,但是这也是有很大的问题的,

下面就是一种出现问题的情况:

在Java指令中创建对象和赋值操作是分开执行的,instance=new Singleton()语句是分两步执行的,但是JVM并不保证这两个操作的执行顺序,也就是说JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例,这样可能会出错,下面举出一个出错的例子。

A、B两个独立的线程

(1)A、B线程同时进入第一个if判断

(2)A首先进入synchronized块,由于instance为null,所以执行instance=new Singleton()

(3)由于JVM的内部优化机制,JVM首先划出了一块内存分配给Singleton实例,但是此时没有初始化,并赋值给instance成员,然后A离开synchronized块

(4)B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给该方法的程序

(5)此时B线程打算使用Singleton实例,却发现它还没有初始化,于是产生了错误


实际情况下,我们使用静态内部类来实现单例模式,JVM的内部机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的,当我们第一次调用getInstance的时候,JVM保证instance只被创建一次,并且会保证把值赋给instance的内存初始化完毕,这样我们就不用担心上面的问题,同时该方法只会在第一次调用的时候使用互斥机制,解决了低性能的问题,下面是改正后的全新系统:

public static synchronized Singleton getInstance() { // 静态方法创建实例
		return instance;
	}

	private static class SingletonFactory {
		private static Singleton instance = new Singleton();
	}

下面还有一种实现方法,可以不用内部类实现:这样也可以保证instance初始化完毕

public static Singleton getInstance() { // 静态方法创建实例
		if (instance == null) {
			syncInit();
		}
		return instance;
	}

	private static synchronized void syncInit() {
		if (instance == null) {
			instance = new Singleton();
		}
	}
总结:

采用类的静态方法和静态类实现单例效果的不同:

(1)静态类不能实现接口

(2)单例可以被延迟初始化,静态类一般在第一次加载的时候初始化,延迟加载的原因是因为一些类太庞大,延迟加载有助于提升性能

(3)单例类可以被继承,它的方法可以被覆盖,但是静态类内部方法都是static,无法被复写。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值