单例模式的介绍、原理、常用的实现方式。
单例模式 介绍 :单例模式的类只有单个对象会被创建(实例化),向其他类只提供一种访问方式。可以全局访问.
解决了什么 :只能有一个实例操作资源。多个打印任务,正在打印的实例只存在一个。
优点 :减小了频繁创建和销毁类(GC)的系统开销。多线安全的单例模式解决了多线程问题。
哪里用到了 :要求只有一个对象的场景.Controller类中的Service类可用单例模式创建对象。Spring用单例模式管理注入容器的类。
如何实现: 使用一个私有静态变量、一个私有构造函数、以及一个公有静态函数来实现。
Implementation(具体实现):
1.懒汉式 优点: 支持lazy loading 。私有静态变量 instance被延迟实例化,节约资源。
缺点: 不支持多线程。多线程环境下是不安全的,如果多个线程同时进入 if (instance == null) ,并且此时instance 为 null,会实例化多次instance。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Singleton { private static Singleton instance; //私有化构造函数 private Singleton (){} //对外提供唯一访问方法 public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
2.饿汉式: 类加载(classloader)就实例化 instance,避免了多线程问题,无lazy loading 的效果。优点: 支持多线程。
缺点: 类加载时就实例化 instance,而不是使用时实例化instance,浪费内存。
1 2 3 4 5 6 7 8 public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }
3.双重校验锁 优点: 支持多线程,有lazy loading效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Singleton { //volatile防止JVM编译器指令重排 private volatile static Singleton instance; private Singleton() {} public static Singleton getinstance() { if (instance == null) { //对实例化的代码加锁 synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
第一个if (instance == null),instance没有被实例化时,对实例化内容加锁。避免非必要加锁。
第二个if (instance == null),第一次实例化instance时,防止多个线程在synchronized处等待并执行instance = new Singleton();
volatile 修饰instance的原因: JVM 具有指令重排的特性,多线程环境下会导致一个线程获得还没有初始化的实例。volatile可禁止JVM指令重排。
具体描述:
instance = new Singleton();
实例化对象的代码分为三个指令执行:
为 instance 分配内存空间
初始化 instance
将 instance 指向分配的内存地址
线程 T1 执行了 1 和 3时,线程T2获取instance,T2会获得一个不该为null的instance。
volatile广泛存在于java并发系列中。
4.静态内部类 优点: 支持lazy loading,多线程安全。实现简单。
缺点: 只适用于静态域的情况。(菜鸟教程提到)
1 2 3 4 5 6 7 8 9 10 11 12 public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
private static class SingletonHolder静态内部类不会初始化对象,多次调用getInstance()得到的是同一个对象INSTANCE。
5.枚举: 缺点: 无lazy loading。优点: 自动支持序列化机制,可防止反射,能避免多线程同步问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class User { //私有化构造函数 private User(){ } //定义一个静态枚举类 static enum SingletonEnum{ //创建一个枚举对象,该对象天生为单例 INSTANCE; private User user; //私有化枚举的构造函数 private SingletonEnum(){ user=new User(); } public User getInstnce(){ return user; } } //对外暴露一个获取User对象的静态方法 public static User getInstance(){ return SingletonEnum.INSTANCE.getInstnce(); } } public class Test { public static void main(String [] args){ System.out.println(User.getInstance()); System.out.println(User.getInstance()); System.out.println(User.getInstance()==User.getInstance()); } } 结果为true
未完成: 静态内部类只适用于静态域,枚举如何避免多线程和反序列化,未防止反射破解单例。