1. 单例设计模式的定义
单例设计模式确保类只有一个实例对象,类本身负责创建自己的对象并向整个系统提供这个实例。在访问这个对象的时候,访问者可以直接获取到这个唯一对象而不必由访问者进行实例化。
单例设计模式保证了全局对象的唯一性,在许多场景中都有应用。例如Windows中多个进程或线程同时操作一个文件的时候,所有的进程或线程对于同一个文件的处理必须通过唯一的实例来进行。
2. java单例设计模式的几种实现方式
单例设计的最大特点是类的构造函数是私有的,确保了只能由类本身创建对象,而访问者无法进行实例化。下面分别介绍五种java中常用的单例设计模式实现方式:
2.1 懒汉式
public class Singleton{ private static Singleton instance; // 类的实例 private Singleton(){}; // 在获取类的实例的时候如果实例还未创建,则创建并返回 public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
是否Lazy初始化(即需要这个域的值的时候才进行初始化):是,在需要实例对象的时候才创建。
是否多线程安全:否
懒汉式是最基本的实现方式,但是线程不安全,如果多个线程访问类的对象,可能某个线程已经创建了但是由于没有同步,其他线程也会创建其他的Singleton对象。因此这种实现只能在单线程条件下工作。
2.2 懒汉式+synchronized同步锁
1 public class Singleton{ 2 private static Singleton instance; 3 private Singleton(){}; 4 5 public static synchronized Singleton getInstance(){ 6 if(instance == null){ 7 instance = new Singleton(); 8 } 9 return instance; 10 } 11 }
是否Lazy初始化:是
是否多线程安全:是
这种方式通过给getInstance()方法加上synchronized同步锁,使得当一个线程在获取对象时其他对象必须等待,等到当前线程释放锁之后其他线程才能获取对象,避免了创建多个对象的问题。但是这种方式由于其他线程必须等待,效率非常低。
2.3 饿汉式
1 public class Singleton{ 2 private static Singleton instance = new Singleton(); 3 private Singleton(){}; 4 5 public static Singleton getInstance(){ 6 return instance; 7 } 8 }
是否Lazy初始化:否
是否多线程安全:是
饿汉式在类加载初始化的时候就创建好了自己的实例对象,除非系统重启类重新加载,否则类会一直维持这唯一一个对象,所以线程是安全的。
2.4 双重校验锁
1 public class Singleton{ 2 private volatile static Singleton instance; 3 private Singleton(){}; 4 5 public static Singleton getInstance(){ 6 if(instance == null){ 7 synchronized (Singleton.class) { 8 if(instance == null){ 9 instance = new Singleton(); 10 } 11 } 12 } 13 return instance; 14 } 15 }
是否Lazy初始化:是
是否多线程安全:是
双重校验锁是对上面第二种方法的一种优化,由于synchronized将整个getInstance()方法锁住导致效率降低,双重校验锁只对需要锁的部分加锁,提高了执行效率。
另一个值得注意的是,这种实现方式中对于instance变量使用了volatile修饰符修饰,查了一下主要是两个作用:
- 保证可见性。使用volatile定义的变量会保证对所有线程都是可见的;
- 禁止指令重排序优化。
更具体的理解可以参考这篇博客。
2.5 静态内部类
1 public class Singleton{ 2 private Singleton(){}; 3 4 private static class InnerSingleton{ 5 private static final Singleton INSTANCE = new Singleton(); 6 } 7 public static final Singleton getInstance(){ 8 return InnerSingleton.INSTANCE; 9 } 10 }
是否Lazy初始化:是
是否多线程安全:是
静态内部类在实现时利用了classloader机制来保证初始化时只有一个线程,并且由于INSTANCE采用了final修饰,一旦被创建便不能修改,保证了对象的唯一性。另外,只有在显示调用getInstance()方法时才会装载InnerSingleton类,从而实例化对象。
参考资料
https://www.runoob.com/design-pattern/singleton-pattern.html
https://www.cnblogs.com/goodAndyxublog/p/11356402.html