1.懒汉式
线程不安全:最基础的实现方式,线程上下文单例,不需要共享给所有线程,也不需要加synchronize之类的锁,以提高性能
package com.yjc.singleton; /** * 单例之懒汉式 * */ public class LazySingleton { //构造私有化 private LazySingleton() { } //类加载的时候不进行初始化 private static LazySingleton lazySingleton=null; public static LazySingleton getLazySingleton(){ //当调用者需要获取一个对象的时候,首先判断当前的对象是否已经进行过实例化了, if (lazySingleton!=null){ //当多个线程同时进入此处的时候,就无法保证是单例的了,因此这种方式是线程非安全的 lazySingleton=new LazySingleton(); } return lazySingleton; } }
2.饿汉式
类加载的时候进行初始化,典型的以空间换时间,线程是安全的,无法做到延迟加载
package com.yjc.singleton; /** * 单例之饿汉式 * */ public class HungrySingleton { //构造私有化 private HungrySingleton() { } private static HungrySingleton hungrySingleton=new HungrySingleton(); //类加载的时候直接实例化 public static HungrySingleton getHungrySingleton() { return hungrySingleton; } }
3.双重锁懒汉模式(Double Check Lock)
双重校验是懒汉式的升级版,通过加锁实现了线程安全,并同时具备延迟加载的机制
package com.yjc.singleton; /** * 单例之双重校验(懒汉式的线程安全版) * */ public class DoubleCheckSingleton { //构造方法私有化 private DoubleCheckSingleton() { } //volatile用于保证内存可见性,所有线程都能看到共享内存的最新状态 private static volatile DoubleCheckSingleton doubleCheckSingleton=null; public static DoubleCheckSingleton getDoubleCheckSingleton(){ if (doubleCheckSingleton!=null){ //synchronized保证同时只能有一个线程进行实例化对象 synchronized (DoubleCheckSingleton.class){ if (doubleCheckSingleton!=null){ doubleCheckSingleton=new DoubleCheckSingleton(); } } } return doubleCheckSingleton; } }
第一次判断doubleCheckSingleton== null为了避免非必要加锁,当第一次加载时才对实例进行加锁再实例化。这样既可以节约内存空间,又可以保证线程安全
在JDK1.6及之后volatile可以解决DCL失效问题,volatile确保单例对象每次均在主内存中读取,这样虽然会牺牲一点效率,但也没有太多影响
4.静态内部类
静态内部类的优点是:在外部类被加载的时候,内部类并不会被立即加载,内部类没有被加载,单例对象也就没有进行实例化,从而也不会占内存。只有在第一次访问内部类中的属性时才会加载内部类
并将内部类中的对象进行实例化。这种方法不仅可以确保线程的安全和对象唯一,也延迟了单例对象的实例化
package com.yjc.singleton; /** * 单例之静态内部类 * */ public class StaticInnerClassSingleton { //构造私有化 private StaticInnerClassSingleton() { } //静态内部类,外部类被加载时,内部类不会被加载 private static class StaticInnerClass { // 静态初始化器,由JVM来保证线程安全 private static StaticInnerClassSingleton staticInnerClassSingleton=new StaticInnerClassSingleton(); } public static StaticInnerClassSingleton getStaticInnerClassSingleton() { //此时开始加载内部类,并将对象进行实例化 return StaticInnerClass.staticInnerClassSingleton; } }
5.枚举
枚举的特性:枚举实例不仅是线程安全的,而且在任何情况下它都是一个单例,枚举的属性都是静态常量。
枚举单例可以自己处理序列化:传统的单例模式的另外一个问题是一旦你实现了serializable接口,他们就不再是单例的了,因为readObject()方法总是返回一个 新的实例对象,就像java中的构造器一样。你可以使用readResolve()方法来避免这种情况
package com.yjc.singleton; public class Singleton { //私有化构造 private Singleton() { } public static Singleton getInstance() { return SingletonEnum.INSTANCE.getInstance(); } private enum SingletonEnum { INSTANCE; private Singleton singleton; // JVM保证这个方法绝对只调用一次 SingletonEnum() { singleton = new Singleton(); } public Singleton getInstance() { return singleton; } } }