Bota5ky

Java笔记(八):单例模式

懒汉式

懒汉式单例模式在第一次调用的时候进行实例化。

1. 适用于单线程环境(不推荐)

此方式在单线程的时候工作正常,但在多线程的情况下就有问题了。如果两个线程同时运行到判断instance是否为null的if语句,并且instance的确没有被创建时,那么两个线程都会创建一个实例,此时类型Singleton就不再满足单例模式的要求了。

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

2. 适用于多线程环境,但效率不高(不推荐)

为了保证在多线程环境下我们还是只能得到该类的一个实例,只需要在getInstance()方法加上同步关键字sychronized,就可以了。但每次调用getInstance()方法时都被synchronized关键字锁住了,会引起线程阻塞,影响程序的性能。

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

3. 双重检验锁

为了在多线程环境下,不影响程序的性能,不让线程每次调用getInstance()方法时都加锁,而只是在实例未被创建时再加锁,在加锁处理里面还需要判断一次实例是否已存在。

public static Singleton getInstance() {
	// 先判断实例是否存在,若不存在再对类对象进行加锁处理
	if (instance == null) {
		synchronized (Singleton.class) {
			if (instance == null) {
				instance = new Singleton();
			}
		}
	}
	return instance;
}

4. 静态内部类方式(推荐)

加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。 由于在调用 StaticSingleton.getInstance() 的时候,才会对单例进行初始化,而且通过反射,是不能从外部类获取内部类的属性的;由于静态内部类的特性,只有在其被第一次引用的时候才会被加载,所以可以保证其线程安全性。
总结:

  • 优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)
  • 劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象
public class StaticSingleton {
	private StaticSingleton() {}
  
	// 获取实例
	public static StaticSingleton getInstance() {
		return StaticSingletonHolder.instance;
	}

	// 一个私有的静态内部类,用于初始化一个静态final实例
	private static class StaticSingletonHolder {
		private static final StaticSingleton instance = new StaticSingleton();
	}

	public void methodA() {}

	public void methodB() {}

	public static void main(String[] args) {
		StaticSingleton.getInstance().methodA();
		StaticSingleton.getInstance().methodB();
	}
}

饿汉式

饿汉式单例类:在类初始化时,已经自行实例化。

1. 饿汉式

public class Singleton {
	private static final Singleton instance = new Singleton();

	private Singleton() {}

	public static Singleton getInstance() {
		return instance;
	}
}

2.枚举方式(推荐)

创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量)。

public class Singleton {
	public static void main(String[] args) {
		Single single = Single.SINGLE;
		single.print();
	}

	enum Single {
		SINGLE;

		private Single() {}

		public void print() {
			System.out.println("hello world");
		}
	}
}
posted @ 2023-05-25 14:19  Bota5ky  阅读(6)  评论(0编辑  收藏  举报