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