单例模式(Singleton Pattern)

单例模式(Singleton Pattern)提供了一种创建对象的最佳方式,属于创建型模式。

特点
1. 单例类只能有一个实例。

2. 单例类必须自己创建自己的唯一实例。

3. 单例类单例类必须给所有其他对象提供这一实例。

实现思路
单例模式要求单例类能够有一个可以返回对象引用(永远是同一个)和获取该实例的方法,这个方法必须是静态方法,通常我们使用getInstance名称。

1. 将单例类的构造方法定义为私有private,这样其他地方的代码就无法通过调用该类的构造方法来实例化该类的对象,只能通过该类提供的静态方法获取该类的唯一实例。

2. 在该单例类中提供一个静态方法,这个静态方法可以返回这个唯一的对象引用。

优点
1. 在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例

2. 提供了对唯一实例的受控访问。

3. 避免对资源的多重占用(比如写文件操作)。

4. 由于在系统内存中只存在一个对象,因此可以节约系统资源,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。

缺点
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

使用场景
1. 需要频繁实例化然后销毁的对象。

2. 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。

3. 频繁访问数据库或文件的对象。

实现方式
1. 懒汉式,线程不安全

public class Singleton {
	private static Singleton instance;
	//私有构造方法
	private Singleton(){}
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}	
}

这是最基本、最简单的实现方式,实现了Lazy Loading的效果,但是这种方式不支持多线程,只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。

2. 懒汉式,线程安全

public class Singleton {
	private static Singleton instance;
	private Singleton () {}
	public static synchronized Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton ();
		}
		return instance;
	}
}

这种方式也实现了Lazy Loading的效果,通过加锁synchronized保证单例,能够在多线程中很好的工作,但是加锁会影响效率,99%情况下不需要同步。

由于这种方式同步效率太低,所以摒弃同步方法,改为同步产生实例化的代码块。代码如下:

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

遗憾的是这种同步并不能起到线程同步的作用。假如一个线程进入了if (instance== null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。

3. 饿汉式

public class Singleton {
	private static Singleton instance = new Singleton();
	private Singleton () {}
	public static Singleton getInstance() {
		return instance;
	}
}

这种方式基于classloader机制,避免了多线程的同步问题。instance在类加载时就实例化,不过,导致导致类加载的原因有很多种,在单例模式中大多数都是调用getInstance()方法,但是也不能确定有其他的方式导致类加载。

4. static静态代码块实现

public class Singleton {
	private Singleton () {}
	private static Singleton instance = null;
	static {
		instance = new Singleton();
	}
	public static Singleton getInstance() {
		return instance;
	}
}

其实跟第3种方式差不多,都是在类加载时就实例化。

5. 静态内部类实现

public class Singleton {
	private Singleton () {}
	private static class InnerHolder {
		private static final Singleton instance = new Singleton();
	}
	public static final Singleton getInstance() {
		return InnerHolder.instance;
	}
}

这种方式同样利用了classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 、4种方式不同的是:第 3、4 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 Lazy Loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为InnerHolder类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 InnerHolder类,从而实例化 instance。想象一下,如果实例化instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 、4种方式就显得很合理。

类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

6. 双重校验锁DCL(Double-checked locking)

public class Singleton {
	private Singleton () {}
	private static volatile Singleton instance;
	public static Singleton getInstance() {
		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}				
			}
		}
		return instance;
	}
}

这种方式采用双锁机制,实现了Lazy Loading的效果,安全且在多线程情况下能保持高性能。进行两次null检查,实例化代码只用执行一次,后面再次访问时,判断if (instance== null),直接return实例化的对象,这样就可以保证线程安全了。
这种方式要求JDK版本1.5起。

7. 枚举

public enum Singleton {
	INSTANCE;
}

借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。

通过Singleton.INSTANCE就可以访问,比调用getInstance()方法简单多了。

这种方式是 Effective Java 的作者 Joshua Bloch 提倡的方式。对于Joshua本人的介绍,百度词条给的是:
Java 集合框架创办人,Joshua Bloch 领导了很多 Java 平台特性的设计和实现,包括 JDK 5.0 语言增强以及屡获殊荣的 Java 集合框架。2004年6月他离开了SUN公司并成为 Google的首席Java架构师。此外他还因为《Effective Java》一书获得著名的 Jolt 大奖。

结构图
在这里插入图片描述





总结到这里,感谢您的阅读~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值