设计模式之路 | 单例模式

单例模式的定义与特点

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。

单例模式有 3 个特点:

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点;

单例模式的结构与实现

单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

下面看单例的实现:

 

1. 饿汉式(静态常量)

public class Singleton01 {

	private static Singleton01 instance;
	
	static {
		instance = new Singleton01();
	}
	
	private Singleton01() { }
	
	public static Singleton01 getInstance() {
		return instance;
	}
}
/**
 * 单例,饿汉式,静态变量
 * 
 * @author Administrator
 */
public class GetSingletonTest {

	public static void main(String[] args) {
		Singleton01 singleton1 = Singleton01.getInstance();
		Singleton01 singleton2 = Singleton01.getInstance();
		System.out.println(singleton1 == singleton2);
	}
}

优缺点说明:

1. 优点:这种写法比较简单,就是在装载的时候就完成实例化。避免了线程同步问题。

2. 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从开始在结束始终都没有使用过这个实例,则会造成内存的浪费。

3. 这种方式基于ClassLoader机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance() 方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式导致类装载,这时候初始化instance就没有达到lazy loading 效果

4. 这种单例模式可用,但可能造成内存浪费。

 

2. 懒汉式(线程安全,同步方法)

public class Singleton02 {

	private static Singleton02 instance;

	private Singleton02() { }

	/**
	 * 添加synchronized,线程安全,防止并发问题
	 */
	public static synchronized Singleton02 getInstance() {
		if (instance == null) {
			instance = new Singleton02();
		}
		return instance;
	}
}

优缺点说明:

1. 解决了线程不安全问题

2. 效率太低了,每个线程在想获取类的实例的时候,执行 getInstance() 方法都要进行同步。而其实这个方法只执行了一次实例代码就够了,后面的想获取该类实例,直接return就行,方法进行同步效率太低。

3. 结论:在实际开发中,不推荐使用

 

3. 懒汉式(DoubleCheck)

/**
 * Double check 写法
 */
public class Singleton03 {

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

优缺点说明:

1. Double-Check概念是多线程开发中常使用到的,如代码所示,进行了两次 if (instance == null) 判断,线程安全。而使用了 volatile 关键字,使得 instance 在各个线程中可见。  

2. 线程安全,延迟加载,效率较高

3. 结论:在实际开发中,推荐使用这种单例模式

 

4. 静态内部类

/**
 * 使用静态内部类实现
 */
public class Singleton04 {

	private Singleton04() {
		
	}
	
	private static class Singleton04Instance {
		private static final Singleton04 INSTANCE = new Singleton04();
	};

	public static synchronized Singleton04 getInstance() {
		return Singleton04Instance.INSTANCE;
	}
}

优缺点说明:

1. 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。

2. 静态内部类方式在 Singleton04 装载时不会立即实例化,而是在调用 getInstance() 方法时,才会加载 Singleton04Instance 类, 从而完成 Singleton04 的实例化。

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

4. 结论:推荐使用

 

5. 枚举方式

public enum Singleton05 {
	INSTANCE;
	public void sayOk() {
		System.out.println("Singleton05 ...");
	}
}

优缺点说明:

1. 这借助 JDK1.5 中添加的枚举来实现单例实例,不仅能避免多线程同步问题,而且还能防止反序列化重新创建对象。

2. 这种方法是 Effective Java 作者 Josh Bloch 所推荐的。

3. 结论:推荐使用

 

单例模式注意事项和说明

1. 单例模式保证了系统内存中该类只存在该类的一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。

2. 当想实例化一个单例的时候,必须要记住使用相应的获取对象的方法,而不是使 new

3. 单例模式使用的场景:需要频繁的进行创建和销毁的对象,创建对象时消耗时过多或耗费资源过多(重量级对象),但有经常使用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值