那些走过的坑

设计模式之单例模式

单例模式是设计模式中最为普遍的模式之一,它用来产生一个对象的具体实例,确保系统中一个类只产生一个实例。

单例模式的好处:

  1. 对于频繁使用的对象,可以减少创建对象所消耗的时间;
  2. 减少new的次数,降低系统内存的使用频率。

如何创建单例类?

  • 必须要有一个private修饰的构造方法,这样可以确保单例不在系统其他代码内被实例化;
  • instance成员变量和getInstance方法必须是static的。

单例模式实现方法:

1、饿汉式

public class Singleton {

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

这种方法很简单很暴力,不用考虑线程安全问题,因为单例对象在JVM加载单例类时就被创建了,而类加载的过程是线程安全的。缺点是无法做到延迟加载,假设单例的创建过程非常慢,但它却在类加载的时候就被创建了,这是相当耗时的。如果系统中始终没有使用到这个单例,那这个加载过程是对资源的一种浪费。并且如果单例类中还有其他static修饰的方法,在调用这些方法的时候,单例也会被创建出来。比如:

public class Singleton {

	private static Singleton instance = new Singleton();
	
	private Singleton() {
		System.out.println("creating singleton...");
	}
	
	public static Singleton getInstance() {
		return instance;
	}
	
        //其他的static方法
	public static void print() {
		System.out.println("hello singleton!");
	}
}

若执行Singleton.print(),将输出:

creating singleton...

hello singleton!

虽然此时并没有使用单例类,但它还是被创建出来。所以可以引入延迟加载机制,例如下面的懒汉式单例。

2、懒汉式(非线程安全)

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

这种方法是非线程安全的,因为在多线程环境下,很有可能当一个线程判断到instance为null时,就进行到单例类的实例化,而实例化过程的时间可能比较长,当还未实例化完时,另外的线程执行if判断,也判断instance为null,于是它也去进行实例化,这样系统中就会存在多个Singleton的实例,就不是单例了。进行同步控制只需要加上synchronized关键字。

3、懒汉式(线程安全)

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

这样可以确保系统在启动时不会有额外的负担,只有当调用getInstance()方法后,单例才会被创建出来,实现了延迟加载。缺点是每次调用getInstance()时都要同步,而且还进行了if判断,在同步状态下的if判断更加耗时。

4、双重校验锁

改善懒汉式的方法是先不进行同步,而是判断当前instance是否被实例化,只有instance未被实例化时才进行同步。

public class Singleton {
	
        //由于instance = new Singleton()并不是一个原子操作,
        //所以需要使用volatile禁止指令重排,从而安全地进行实例化
	private volatile static Singleton instance;
	
	private Singleton() {
		
	}
	
	public static Singleton getInstance() {
		if(instance == null) {
			synchronized(Singleton.class) {
				if(instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

这样可以确保只有在第一次实例化instance时才进行同步,其他时候直接返回单例。既实现了线程安全,也实现了延迟加载,并且不会对性能有太大的影响。

5、静态内部类

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

内部类SingletonHolde在第一次调用getInstance()方法时才会被加载,实现了延迟加载,并且是线程安全的。且在其加载过程中实例化了一次INSTANCE,由于INSTANCE是静态常量,所以以后再调用getInstance()方法会直接返回其引用。

单例模式应用场景:

  • 打印机
  • 回收站
  • 任务管理器
  • 文件系统
  • 系统日志对象
  • 网站计数器
  • 数据库连接池
  • 多线程线程池
  • 系统配置文件对象

等等等等。。。

发表评论

电子邮件地址不会被公开。 必填项已用*标注