一、什么是单例模式:
单例模式是一种确保了一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。被实例化的类称为单例类。
二、单例模式的特点:
- 单例类只有一个实例。
- 单例类必须自行创建自己唯一的实例。
- 单例类必须给其他对象提供这个实例。
注意:虽然单例模式和单例类限定了只能有一个实例,但是作为单例模式的推广,可以推广到任意且有限多个实例的情况,这时候被称为多例模式和多例类。
三、单例模式的结构:
- 一个单例类只有一个实例。
- 单例类的实例持有对自己的引用。
四、单例模式的实例化:
Java中单例模式有着自己的特点,具体表现在单例类的实例化上:
饿汉式单例类(静态常量):
1 /** 2 * 饿汉式(静态常量) 3 * 4 * @author ZhouDX 5 * @since 2019/3/4 22:12:28 6 */ 7 public class HungerSingleton { 8 private static final HungerSingleton SINGLETON= new HungerSingleton(); 9 10 /** 11 * 私有的默认构造函数 12 */ 13 private HungerSingleton() { 14 } 15 16 /** 17 * 静态工厂方法 18 */ 19 public static HungerSingleton getInstance() { 20 return SINGLETON; 21 } 22 }
Java中最简单的单例类,类的单例被声明为静态变量,在类加载时,调用类的私有构造函数,静态变量被实例化。
特点:
1.类的构造函数私有,避免了外界利用构造函数创建任意多的实例。
2.且由于构造函数私有,类不能被继承。
3.只能通过静态方法getInstance()来获取类的实例对象。
优点:
类装载的时候就完成实例化。避免了线程同步问题。
缺点:
在类装载的时候就完成实例化,没有达到延迟加载的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
饿汉式单例类(静态代码块):
1 /** 2 * 饿汉式单例类(静态代码块) 3 * 4 * @author ZhouDX 5 * @since 2019/3/13 22:45:24 6 */ 7 public class HungerSington_StaticCode { 8 private static HungerSington_StaticCode singleton; 9 10 /** 11 * 静态代码块 12 */ 13 static { 14 singleton= new HungerSington_StaticCode(); 15 } 16 17 /** 18 * 私有构造函数 19 */ 20 private HungerSington_StaticCode() { 21 } 22 23 /** 24 * 获取单例类实例的唯一接口 25 * 26 * @return 单例类 27 */ 28 public static HungerSington_StaticCode getInstance() { 29 return singleton; 30 } 31 }
特点:
将单例类放在静态代码块中,也是类在加载时执行静态代码块中的代码,完成类的实例化,优缺点同静态常量。
汉懒式单例类(线程不安全):
1 /** 2 * 懒汉式(线程不安全)[不可用] 3 * 4 * @author ZhouDX 5 * @since 2019/3/13 22:52:24 6 */ 7 public class LazySingleton_ThreadUnsafe { 8 private static LazySingleton_ThreadUnsafe singleton; 9 10 /** 11 * 获取单例类实例 12 * 13 * @return 单例类实例 14 */ 15 public static LazySingleton_ThreadUnsafe getInstance() { 16 if (null == singleton) { 17 singleton = new LazySingleton_ThreadUnsafe(); 18 } 19 20 return singleton; 21 } 22 }
特点:
1.达到了延迟加载的目的,只有在单例类第一次被引用时将自己实例化。
2.在单线程下使用。
缺点:
在多线程的环境中,多个线程同时进入if (null == singleton) {},还未执行singleton = new LazySingleton_ThreadUnsafe()时,另一个线程也恰好进入这里,就会造成单例类多个实例,线程不安全,不可以再多线程的环境下使用。
懒汉式(线程安全,同步方法)
1 /** 2 * 懒汉式(线程安全,同步方法) 3 * 4 * @author ZhouDX 5 * @since 2019/3/4 22:23:02 6 */ 7 public class LazySingleton { 8 private static LazySingleton lazySingleton = null; 9 10 /** 11 * 构造函数 12 */ 13 private LazySingleton() { 14 } 15 16 /** 17 * 静态工厂方法,返回懒汉式实力类的唯一实例 18 * 19 * @return 20 */ 21 public static synchronized LazySingleton getInstance() { 22 if (lazySingleton == null) { 23 return lazySingleton = new LazySingleton(); 24 } 25 return lazySingleton; 26 } 27 }
特点:
使用了synchronized对静态工厂类方法进行了同步,安全处理多线程的问题。
缺点:
每个线程执行getInstance()方法都要进行同步,大大降低了执行的效率。且getInstance()只需要实例化一次就可以。
懒汉式(线程不安全,同步代码块)
1 /** 2 * 懒汉式(线程安全,同步代码块) 3 * 4 * @author ZhouDX 5 * @since 2019/3/13 23:12:08 6 */ 7 public class LaSingleton_ThreadUnsafe { 8 private static LaSingleton_ThreadUnsafe singleton; 9 10 /** 11 * 静态构造方法 12 */ 13 private LaSingleton_ThreadUnsafe() { 14 } 15 16 /** 17 * 获取单例类实例 18 * 19 * @return 单例类实例 20 */ 21 public static LaSingleton_ThreadUnsafe getInstance() { 22 if (null == singleton) { 23 synchronized (LaSingleton_ThreadUnsafe.class) { 24 singleton = new LaSingleton_ThreadUnsafe(); 25 } 26 } 27 return singleton; 28 } 29 }
特点:
同步产生实例的代码块。
缺点:
假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
懒汉式(双重检查):
1 /** 2 * 懒汉式(双重检查): 3 * 4 * @author ZhouDX 5 * @since 2019/3/13 23:24:27 6 */ 7 public class LazySingleton_DoubleCheck { 8 private static volatile LazySingleton_DoubleCheck singleton; 9 10 /** 11 * 静态构造方法 12 */ 13 private LazySingleton_DoubleCheck() { 14 } 15 16 /** 17 * 获取单例类实例 18 * 19 * @return 单例类实例 20 */ 21 public static LazySingleton_DoubleCheck getInstance() { 22 if (null == singleton) { 23 synchronized (LazySingleton_DoubleCheck.class) { 24 singleton = new LazySingleton_DoubleCheck(); 25 } 26 } 27 return singleton; 28 } 29 }
特点:
1.进行了两次if (singleton == null)检查,保证了线程安全。
2.实例化代码只执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象。
优点:
程安全;延迟加载;效率较高。
静态内部类:
1 /** 2 * 静态内部类 3 * 4 * @author ZhouDX 5 * @since 2019/3/13 23:28:58 6 */ 7 public class Singleton_StaticInnerClass { 8 /** 9 * 私有构造方法 10 */ 11 private Singleton_StaticInnerClass() { 12 } 13 14 /** 15 * 静态内部类 16 */ 17 private static class SingletonInstance { 18 private static final Singleton_StaticInnerClass SINGLETON = new Singleton_StaticInnerClass(); 19 } 20 21 /** 22 * 获取单例类实例 23 * 24 * @return 单例类实例 25 */ 26 private static Singleton_StaticInnerClass getInstance() { 27 return SingletonInstance.SINGLETON; 28 } 29 }
特点:
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
优点:
避免了线程不安全,延迟加载,效率高。
枚举:
1 /** 2 * 枚举 3 * 4 * @author ZhouDX 5 * @since 2019/3/13 23:33:43 6 */ 7 public enum Singleton_Enum { 8 SINGLETON; 9 10 public void whateverMethod() { 11 } 12 }
特点:
系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
优点:
当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。
懒汉式单例类与饿汉式单例类的比较:
- 饿汉式单例类在自己被加载时将自己实例化,即便加载器是静态的,依旧在加载时实例化自己;懒汉式单例类在第一次被引用时将自己实例化,如果加载器是静态的,懒汉式单例类被加载时不会将自己实例化。
- 从资源利用角度讲,懒汉式单例类的资源利用效率高点;从速度和反应时间来讲,饿汉式的好点。