java设计模式1-单例模式

单例模式概述

类的对象只能由该类创建,且该类只有唯一的对象。外部想要访问该对象只能通过类的公开的方法访问。

单例模式的条件

1、类的对象只能由该类创建 
2、类的对象只能有一个
3、该类提供一个外部可访问该对象的方法

使用场景

单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如: 
1.需要频繁实例化然后销毁的对象。 
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
3.有状态的工具类对象。 
4.频繁访问数据库或文件的对象

使用场景原文链接

代码实现

/**懒汉式
 * 單例模式-線程不安全
 * @author Morth
 *
 */
public class LazyLoadingUnThreadSafe {
	//將構造方法私有化,使其無法被實例化
	private LazyLoadingUnThreadSafe() {}
	//創建一個實例
	private static LazyLoadingUnThreadSafe singleLazy;
	//獲取實例
	public static LazyLoadingUnThreadSafe getInstance() {
		if ( singleLazy == null ) {
			singleLazy = new LazyLoadingUnThreadSafe();
		}
		return singleLazy;
	}
}

通过上面的代码很容易看出,该方式并不是线程安全的,这种情况在多线程中无法正常使用。
我们来看第二种线程安全的方式,即给 getInstance() 方法加上 synchronized 关键字

/**懒汉式
 * 單例模式-線程安全
 * @author Morth
 *
 */
public class LazyLoadingThreadSafe {
	//將構造方法私有化,使其無法被實例化
	private LazyLoadingThreadSafe() {}
	//創建一個實例
	private static LazyLoadingThreadSafe singleLazy;
	//獲取實例-synchronized 鎖機制,線程安全
	public static synchronized LazyLoadingThreadSafe getInstance() {
		if ( singleLazy == null ) {
			singleLazy = new LazyLoadingThreadSafe();
		}
		return singleLazy;
	}
}

加了 synchronized 关键字后实现是线程安全,但又会出现另一个问题。此方式因加了 synchronized ,获取对象的效率会大大降低。那如何优化呢?
我们可以对 getInstance() 进行简单的优化。

public static LazyLoadingThreadSafe getInstance() {
	if( singleLazy == null ){
		synchronized(LazyLoadingThreadSafe.class) {
			if ( singleLazy == null ) {
				singleLazy = new LazyLoadingThreadSafe();
			}
		}
	}
	return singleLazy;
}

修改代码后可以发现,当外部调用 getInstance() 后并不会立即锁住该方法,而是先对类对象进行判断,如果不为 null 则立即返回该对象。这样在保证线程安全的前提下提升了效率。但还存在一个问题就是如果在并发情况下且类对象为 null 时;假设代码执行到 singleLazy = new LazyLoadingThreadSafe();;此时又有其他的并发请求进来然而对象尚未创建完成,其他请求就只能等待线程释放锁,这降低了效率。因此我们可以为对象加上关键字 volatile (个人理解该关键字是当对象被修改时,其他对象可以马上知道;理解不到位轻喷~ - ~).;这样,当其他请求进来时,类对象刚好被创建,该对象就不会判断为空,从未进入等待。最后的优化结果为:

/**雙重校驗鎖
 * 單例模式
 * @author Morth
 *
 */
public class LazyLoadingThreadSafe {
	//將構造方法私有化,使其無法被實例化
	private LazyLoadingThreadSafe() {}
	//創建一個實例
	private volatile static LazyLoadingThreadSafe singleLazy;
	//獲取實例-synchronized 鎖機制,線程安全
	public static LazyLoadingThreadSafe getInstance() {
		if( singleLazy == null ){
			synchronized(LazyLoadingThreadSafe.class) {
				if ( singleLazy == null ) {
					singleLazy = new LazyLoadingThreadSafe();
				}
			}
		}
	return singleLazy;
	}
}

单例模式除了上面的3种实现方式外还可以有以下了的方式:

/**饿汉式
 * 單利模式
 * @author Morth
 *
 */
public class HungeryLoad {
	//私有化構造方法,使其無法被實例化
	private  HungeryLoad() {}
	//創建實例
	private static HungeryLoad hungeryLoad=new HungeryLoad();
	//獲取實例
	public static HungeryLoad getInstance() {
		return hungeryLoad;
	} 
}

该方式也是线程安全的,当类加载时就会实例化该对象,所以容易产生垃圾对象,占用内存。与之相对应的,为了防止类加载时就将对象实例化,我们可以将对象的实例化放入一个静态内部类中:

/**靜態內部類
 * 單利模式
 * @author Morth
 *
 */
public class HungeryLoad {
	//私有化構造方法,使其無法被實例化
	private  HungeryLoad() {}
	private static class StaticInnerClassHolder{
		//創建實例
		private static final HungeryLoad hungeryLoad=new HungeryLoad();
	}
	//獲取實例
	public static HungeryLoad getInstance() {
		return StaticInnerClassHolder.hungeryLoad;
	} 
}

当类加载时对象并没有被实例化,只有通过 StaticInnerClassHolder.hungeryLoad 调用该对象时内部类才会被加载,从而实例化类对象。
最后一种单例模式的实现:枚举类

/**枚举类
 * 單利模式
 * @author Morth
 *
 */
public enum EnumClass {
	INSTANCE;
	public EnumClass getInstance() {
		return SingletonEnum.INSTANCE;
	}
}

利用枚举类的特性我们就可以很简单的实现一个单例模式。首先枚举类时线程安全的且枚举类的构造方法是 private 的;EnumClass 反编译后的类大概是这样的

/**枚举类
 * 單利模式
 * @author Morth
 *
 */
public final class EnumClass extends Enum{

	private static final INSTANCE;
	//构造方法
	private EnumClass (String name,int i){
		super(name,i);
	}
	//静态块,初始化 *INSTANCE*
	static{
		INSTANCE=new EnumClass("INSTANCE",0)
	}
	
	public SingletonEnum getInstance() {
		return SingletonEnum.INSTANCE;
	}
}

优缺点

优点:

1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例 
2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。 
3.提供了对唯一实例的受控访问。 
4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。 
5.允许可变数目的实例。 
6.避免对共享资源的多重占用。 

缺点:

1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。 
2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。 
3.单例类的职责过重,在一定程度上违背了“单一职责原则”。 
4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。 

使用注意事项:

1.使用时不能用反射模式创建单例,否则会实例化一个新的对象 
2.使用懒单例模式时注意线程安全问题 
3.单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式)

优缺点参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值