#头条创作挑战赛#前言 单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,使用单例模式的类只有一个对象实例。1、饿汉单例 非多线程安全,非懒加载 饿汉需要直接创建实例。public class HungryHanModel { private static HungryHanModel hungryHanModel = new HungryHanModel(); private HungryHanModel() {} public static HungryHanModel getInstance(){ return hungryHanModel; } } 缺点: 类加载就初始化,浪费内存 优点: 没有加锁,执行效率高。还是线程安全的实例 2、懒汉单例 非线程安全的懒汉单例,懒加载 懒汉单例:在类初始化不会创建实例,只有被调用时才会创建实例。public class LazyManMode { private static LazyManMode lazyManMode; private LazyManMode() {} public static LazyManMode getInstance() { if (lazyManMode == null) { lazyManMode = new LazyManMode(); } return lazyManMode; } } 实例在调用 getInstance 才会创建实例,优点是不占内存,在单线程模式下,是安全的。但是多线程模式下,多个线程同时执行 if (lazyManMode== null) 进行判空,结果都为 true,会创建多个实例,所以上面的懒汉单例是一个线程不安全的实例。 3、加同步锁的懒汉单例 多线程安全,懒加载 为了解决多个线程同时执行 if (lazyManMode== null) 的问题,getInstance 方法添加同步锁,这样多条线程排队进入了 getInstance 方法,只有执行完毕之后,其他线程才能进入该方法,同一时间只有一个线程才能进入该方法。public class LazyManModeSync { private static LazyManModeSync lazyManlModeSync; private LazyManModeSync() {} public static synchronized LazyManModeSync getInstance() { if (lazyManlModeSync == null) { lazyManlModeSync =new LazyManModeSync(); } return lazyManlModeSync; } } 这样配置虽然保证了线程的安全性,但是效率低,只有在第一次调用初始化之后,才需要同步,初始化之后都不需要进行同步。锁的粒度太大,影响了程序的执行效率。 4、双重检验懒汉单例 多线程安全,懒加载 使用 synchronized 声明的方法,在多个线程访问,比如第一条线程访问时,其他线程必须等待第一条线程执行完毕之后才能访问,这样会大大的降低的程序的运行效率。这个时候在加了同步锁的懒汉模式进行锁粒度的一个优化,锁住方法中的部分代码块,优化时间。 双重检验首先判断实例是否为空,然后使用 synchronized (lazyManModeDoubleCheck.class) 使用类锁,锁住整个类,执行完代码块的代码之后,新建了实例,其他代码都不走 if (lazyManModeDoubleCheck== null) 里面,在最开始的时候程序创建对象效率会低下。而 synchronized 里面再次判断是否可能同时有多个线程都执行到 synchronized (lazyManModeDoubleCheck.class) ,如果其中一条线程线程新建实例,那么其他线程 的lazyManModeDoubleCheck就不为空,就不会再创建实例了。public class LazyManModeDoubleCheck { private static LazyManModeDoubleCheck lazyManModeDoubleCheck; private LazyManModeDoubleCheck() {} public static LazyManModeDoubleCheck getInstance() { if (lazyManModeDoubleCheck == null) { synchronized (LazyManModeDoubleCheck.class) { if (lazyManModeDoubleCheck == null) { lazyManModeDoubleCheck = new LazyManModeDoubleCheck(); } } } return lazyManModeDoubleCheck; } } 5、静态内部类 多线程安全,懒加载 外部类加载时,并不会加载内部类,也就不会执行 new CreateASingleInstance(),这属于懒加载。只有第一次调用 getInstance() 方法时才会加载 CreateASingleInstance类。而静态内部类是线程安全的。 静态内部类为什么是线程安全 静态内部类利用了类加载机制的初始化阶段 方法,静态内部类的静态变量赋值操作,实际就是一个方法,当执行getInstance() 方法时,虚拟机才会加载 CreateASingleInstance静态内部类, 然后在加载静态内部类,该内部类有静态变量,JVM会改内部生成方法,然后在初始化执行方法 —— 即执行静态变量的赋值动作。 虚拟机会保证 方法在多线程环境下使用加锁同步,只会执行一次 方法。 这种方式不仅实现延迟加载,也保障线程安全。public class StaticInnerClass { private StaticInnerClass() {} private static class CreateASingleInstance { private static final CreateASingleInstance INSTANCE = new CreateASingleInstance(); } public static final CreateASingleInstance getInstance() { return CreateASingleInstance.INSTANCE; } }