Java 实现单例模式

Java 实现单例模式

前置了解

  • 单例模式(Singleton Pattern):保证一个类只有一个实例,并提供一个访问它的全局访问点

  • 单例模式下的类都是只创建一个唯一实例的。

  • 饿汉单例模式:在程序开始运行时,就将该单例对象加载到内存,即预先加载

  • 懒汉单例模式:使用到这个类时才创建实例,即懒加载

破坏单例模式

  • 无论是通过懒汉式还是饿汉式实现的单例模式,都可能通过反射反序列化破坏掉单例的特性,可以创建多个对象
反射破坏单例模式
  • 反射破坏单例模式: 利用反射,可以强制访问单例类的私有构造器,创建新的对象。

  • 原理:通过调用构造方法生成新的对象

  • 阻止方案:在构造方法中添加 if 判空,满足实例不存在才创建实例。

反序列化破坏单例模式
  • 反序列化破坏单例模式: 通过 readObject 方法读取对象时会返回一个新的对象实例。

  • 原理:单例类实现了序列化接口 Serializable, 就可以通过反序列化破坏单例;默认的反序列化方法 readObject 会返回一个新的对象实例

  • 阻止方案 1:不需要序列化时,就不实现序列化接口 Serializable

  • 阻止方案 2:重写反序列化方法 readObject,让其直接返回单例实例对象

总结

  • 注意:在整个单例模式实现中,统一用 getInstance 方法获取该类的单例实例。

  • 构造方法都是私有的(private)。

  • static 表示 共享,final 表示不可变,private 表示私有。

饿汉单例模式

  • 优点:没有加锁,执行效率高;线程安全

  • 缺点:类加载就初始化,getInstance 方法只是将对象输出,浪费内存。

  • 直接创建一个 静态不可变常量 来存储类实例,在声明时直接赋值

懒汉单例模式

  • 共有的优点:实例在调用 getInstance 方法时才会创建实例,这样是不占内存的。
普通懒汉模式
  • 优点:没有加锁,执行效率高。

  • 缺点:线程不安全。

  • 创建一个 静态常量 来存储类实例,初始值为 null

  • if判断是否为 null,是则 创建类实例,否则 无操作。

加了同步锁的懒汉模式
  • 优点:线程安全。

  • 缺点:锁的粒度太大,影响了程序的执行效率,效率低

  • 创建一个 静态常量 来存储类实例,初始值为 null

  • if判断是否为 null,是则 创建类实例,否则 无操作。

  • 在 getInstance 方法上加 synchronized 锁

  • 这样线程就必须排队使用 getInstance 方法

使用类锁的懒汉模式
  • 优点:线程安全;使用 synchronized 代码块优化执行时间,减少锁的粒度。

  • 创建一个 静态常量 来存储类实例,初始值为 null

  • if判断是否为 null,是则 创建类实例,否则 无操作。

  • 第一个 if 判空 用来阻止实例创建完成后,后续的线程进入 创建类实例流程。

  • 第二个 if 判空 用来阻止实例创建完成后,前面在 synchronized 代码块排队的线程进入 创建类实例流程。

  • synchronized 代码块 是方法 synchronized 的优化版,在关键代码位置添加,功能上没有差别,但效率却提高了

使用内部类的懒汉模式
  • 优点:没有加锁,执行效率高;线程安全;不依赖 JDK 版本;外部类加载时,并不会加载内部类实例,而是在调用 getInstance 方法时才会 new 内部类

  • 缺点:是通过虚拟机来保证方法使用锁,来保证线程安全,会增加 JVM 压力。

  • 类实例存储在 类的私有内部类的 静态不可变常量 中,即单例持有者是:内部类

  • 内部类的 静态不可变常量的创建是在第一次调用 getInstance 方法时开始的。

  • 内部类在实现中是没有创建的,因为不需要,如果要创建,那是在第一次调用 getInstance 方法时开始创建。

  • 静态内部类模式创建单例类实例是使用 JVM 机制保证线程安全

使用枚举类的懒汉模式
  • 优点:唯一一种不会被破坏的单例实现模式;线程安全;没有加锁,执行效率高;外部类加载时,并不会加载内部枚举类实例,而是在调用 getInstance 方法时才会 new 内部枚举类

  • 缺点:JDK 1.5 后才能使用。

  • 枚举实例只能装载一次,同样,实例的属性也不会变

  • 类实例存储在 类的私有内部枚举类的 属性 中,即单例持有者是:内部枚举类

  • 内部枚举类的创建是在第一次调用内部枚举类的 getInstance 方法时开始的。

  • 不需要额外的操作即可防止破环单例模式

实现

饿汉单例模式

  • 类代码
class HungrySingletonMode {
	
	private static final HungrySingletonMode HUNGRY_SINGLETON_MODE = new HungrySingletonMode();
	
	private HungrySingletonMode() {
	}
	
	public static HungrySingletonMode getInstance() {
		return HUNGRY_SINGLETON_MODE;
	}
	
	public String getClassIntroduce() {
		return "className:" + HungrySingletonMode.class + ";Introduce:普通的饿汉单例模式";
	}

}
  • 怎么获取实例?
public class Main {
	
	public static void main(String[] args) {
		HungrySingletonMode hungrySingletonMode = HungrySingletonMode.getInstance();
		System.out.println(hungrySingletonMode.getClassIntroduce());
	}

}

懒汉单例模式(普通实现)

  • 类代码
class LazySingletonMode {
	
	private static LazySingletonMode LAZY_SINGLETON_MODE = null;
	
	private LazySingletonMode() {
	}
	
	public static LazySingletonMode getInstance() {
		// 第一次加载时才创建
		if(LAZY_SINGLETON_MODE == null) {
			LAZY_SINGLETON_MODE = new LazySingletonMode();
		}
		return LAZY_SINGLETON_MODE;
	}
	
	public String getClassIntroduce() {
		return "className:" + LazySingletonMode.class + ";Introduce:普通的懒汉单例模式";
	}

}
  • 怎么获取实例?
public class Main {
	
	public static void main(String[] args) {
		LazySingletonMode lazySingletonMode = LazySingletonMode.getInstance();
		System.out.println(lazySingletonMode.getClassIntroduce());
	}

}

懒汉单例模式(同步锁实现)

  • 类代码
class LazySingletonMode1 {
	
	private static LazySingletonMode1 LAZY_SINGLETON_MODE_1 = null;
	
	private LazySingletonMode1() {
	}
	
	public static synchronized LazySingletonMode1 getInstance() {
		// 在 LazySingletonMode 的基础上,加了 synchronized(同步锁)
		if(LAZY_SINGLETON_MODE_1 == null) {
			LAZY_SINGLETON_MODE_1 = new LazySingletonMode1();
		}
		return LAZY_SINGLETON_MODE_1;
	}
	
	public String getClassIntroduce() {
		return "className:" + LazySingletonMode1.class + ";Introduce:加了同步锁的懒汉单例模式";
	}

}
  • 怎么获取实例?
public class Main {
	
	public static void main(String[] args) {
		LazySingletonMode1 lazySingletonMode1 = LazySingletonMode1.getInstance();
		System.out.println(lazySingletonMode1.getClassIntroduce());
	}

}

懒汉单例模式(类锁实现)

  • 类代码
class LazySingletonMode2 {
	
	// 添加 volatile 是为了禁止 指令重排,解决 指令重排 问题。
	// volatile 是 JVM 提供的轻量级同步机制
	// 作用是: 保证可见性;禁止指令重排;但不保证原子性
	private static volatile LazySingletonMode2 LAZY_SINGLETON_MODE_2 = null;
	
	private LazySingletonMode2() {
	}
	
	public static LazySingletonMode2 getInstance() {
		// 在 LazySingletonMode1 的基础上,将 synchronized(同步锁)改为:使用 synchronized 声明的方法
		// 双重检验首先判断实例是否为空,然后使用 synchronized (class) 使用类锁,锁住整个类。
		// 执行完代码块的代码之后,新建了实例后,因为第二重 if 其他代码都不走 if () 里面,只会在最开始的时候效率变慢。
		if(LAZY_SINGLETON_MODE_2 == null) {
			synchronized(LazySingletonMode2.class) {
				if(LAZY_SINGLETON_MODE_2 == null) {
					LAZY_SINGLETON_MODE_2 = new LazySingletonMode2();
				}
			}
		}
		return LAZY_SINGLETON_MODE_2;
	}
	
	public String getClassIntroduce() {
		return "className:" + LazySingletonMode2.class + ";Introduce:使用类锁的懒汉单例模式";
	}

}
  • 怎么获取实例?
public class Main {
	
	public static void main(String[] args) {
		LazySingletonMode2 lazySingletonMode2 = LazySingletonMode2.getInstance();
		System.out.println(lazySingletonMode2.getClassIntroduce());
	}

}

懒汉单例模式(内部类实现)

  • 类代码
class LazySingletonMode3 {
	
	private LazySingletonMode3() {
		System.out.println("className:" + LazySingletonMode3.class + " 创建了!");
	}
	
	public static LazySingletonMode3 getInstance() {
		return SingletonHolder.LAZY_SINGLETON_MODE_3;
	}
	
	public String getClassIntroduce() {
		return "className:" + LazySingletonMode3.class + ";Introduce:使用内部类的懒汉单例模式";
	}
	
	/**
	 * 内部类
	 */
	public static class SingletonHolder {
		
		private static final LazySingletonMode3 LAZY_SINGLETON_MODE_3 = new LazySingletonMode3();
		
		public SingletonHolder() {
			System.out.println("className:" + SingletonHolder.class + " 创建了!");
		}
	}

}
  • 怎么获取实例?
public class Main {
	
	public static void main(String[] args) {
		LazySingletonMode3 lazySingletonMode3 = LazySingletonMode3.getInstance();
		System.out.println(lazySingletonMode3.getClassIntroduce());
	}

}

懒汉单例模式(枚举实现)

  • 类代码
class LazySingletonMode4 {
	
	private LazySingletonMode4() {
		System.out.println("className:" + LazySingletonMode4.class + " 创建了!");
	}
	
	public static LazySingletonMode4 getInstance() {
		return SingletonHolder.INSTANCE.getInstance();
	}
	
	public String getClassIntroduce() {
		return "className:" + LazySingletonMode4.class + ";Introduce:使用内部类的懒汉单例模式";
	}
	
	/**
	 * 枚举
	 * 

* 枚举类型是线程安全的,并且只会装载一次 */ private enum SingletonHolder { /** * 表示实例,每一个枚举只会装载一次 */ INSTANCE; private final LazySingletonMode4 instance; SingletonHolder() { this.instance = new LazySingletonMode4(); System.out.println("className:" + SingletonHolder.class + " 创建了!"); } private LazySingletonMode4 getInstance() { return instance; } } }

  • 怎么获取实例?
public class Main {
	
	public static void main(String[] args) {
		LazySingletonMode4 lazySingletonMode4 = LazySingletonMode4.getInstance();
		System.out.println(lazySingletonMode4.getClassIntroduce());
	}

}

引用与参考

  • 文章参考:单例模式的六种实现方式!

  • 文章参考:为什么用枚举类来实现单例模式越来越流行?

  • 文章参考:5种方式实现 Java 单例模式

你可能感兴趣的:(通用的知识,Java,学习笔记,单例模式,java)