创造型模式主要的关注点是怎么创建对象,主要特点是将对象的创建和使用分离开来。以此降低系统的耦合度,使用者可以不需要过分关注对象的创建细节,直接使用就可以。
创造模式例子:
单例模式是创建型模式的其中一个模式,一般只一个类只有一个实例(只实例化一次),并且这个这个实例化的过程是在这个类本身完成的,只是对外开放了一个获取这个实例了的对象的方法。
单例模式例子:
懒汉式单例是指类在用的时候才会实例化,并且只有在第一次被调用时会实例化一次,多次调用都是返回第一次实例化的对象。
一下是简单地一个样例,该例子可以在单线程模式下保持全局单例:
//代码段1:单线程懒汉模式单例 package com.lqh.singleton; /** * 单例模式-懒汉式 * @author lqh * @version 1.0 * @date: 2021/7/9 21:23 */ public class LazySingleton { /** * 在类内私有化一个静态对象,也就是唯一的实例,外面获取的实例都是指向这个对象 */ private static LazySingleton instance; /** * 构造方法私有化,防止在其他地方被实例化 * 此处虽然私有化了构造方法,但是还是能被反射获取到并调用 */ private LazySingleton(){} /** * 返回本类的实例对象,如果对象为空代表还没有被实例化, * 就需要调用构造方法实例化后返回 * @return */ public static LazySingleton getInstance(){ if (instance == null){ instance = new LazySingleton(); } return instance; } }
然后运行测试一下
//代码段2:单线程测试懒汉模式单例 package com.lqh; import com.lqh.singleton.LazySingleton; import java.util.ArrayList; import java.util.List; /** * 主类 * @date 22:24 2021/7/9 * @author lqh */ public class Main { /** * 比较两个对象是否相同 * @param i1 * @param i2 */ private static void equals(LazySingleton i1, LazySingleton i2){ if (!i1.equals(i2)){ System.out.println("有不同的实例!"); } } public static void main(String[] args) { //获取对象 LazySingleton instance1 = LazySingleton.getInstance(); //打印哈希码值 System.out.println(instance1.toString());//com.lqh.singleton.LazySingleton@1b6d3586 LazySingleton instance2 = LazySingleton.getInstance(); //打印哈希码值 System.out.println(instance2.toString());//com.lqh.singleton.LazySingleton@1b6d3586 //比较两个对象是否相同 System.out.println(instance1.equals(instance2));//true //获取十次并加入列表 List<LazySingleton> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { //获取实例 LazySingleton instance = LazySingleton.getInstance(); //将获取的实例加入列表 list.add(instance); } //遍历列表打印每个实例的哈希码值 list.forEach(x -> System.out.println(x));//无一例额外全部都是com.lqh.singleton.LazySingleton@1b6d3586 //遍历列表将每个实例都与列表内的所有实例比对一遍,有不同的就输出不同 list.forEach(x -> { for (LazySingleton instance : list) { equals(x, instance);//全部相同 } }); } }
运行结果:
com.lqh.singleton.LazySingleton@1b6d3586 com.lqh.singleton.LazySingleton@1b6d3586 true com.lqh.singleton.LazySingleton@1b6d3586 com.lqh.singleton.LazySingleton@1b6d3586 com.lqh.singleton.LazySingleton@1b6d3586 ...
上面可以看到在main线程下生成的所有LazySingleton对象都指向了同一个哈希码值的对象,毕竟他们都是调用的同一个方法(getInstance):
//代码段1片段 public static LazySingleton getInstance(){ if (instance == null){ instance = new LazySingleton(); } return instance; }
并且返回的也都是instance这个对象:
//代码段1片段 private static LazySingleton instance;
但是如果在多线程模式下这个方法就不在适用,因为多线程模式下可以看做是两个或多个线程同时在执行,比如两个线程A和B,线程A在调用getInstance()方法的时候检测到instance对象为空实例化了一个对象,线程B在A正在实例化对象的时候也调用了getInstance()方法,此时A线程还没有实例化完成,导致B线程也检测到instance对象时空,也会进行实例化,这就导致了两个对象的生成,打破了单例模式。
多线程测试:
//代码段3:多线程测试懒汉模式单例 package com.lqh; import com.lqh.singleton.LazySingleton; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 主类 * @date 22:24 2021/7/9 * @author lqh */ public class Main { /** * 比较两个对象是否相同 * @param i1 * @param i2 */ private static void equals(LazySingleton i1, LazySingleton i2){ if (!i1.equals(i2)){ System.out.println("有不同的实例!"); } } public static void main(String[] args) { //创建一个线程安全的列表,用来保存实例对象 List<LazySingleton> list = Collections.synchronizedList(new ArrayList<>()); //创建十个线程,且每个线程获取LazySingleton实例 for (int i = 0; i < 10; i++) { new Thread(() ->{ LazySingleton instance = LazySingleton.getInstance(); list.add(instance); }, "线程" + (i+1)).start(); } //遍历列表打印每个实例的哈希码值 list.forEach(x -> System.out.println(x)); //遍历列表比较每一个实例是否相同 list.forEach(x -> { for (LazySingleton instance : list) { equals(x,instance); } }); } }
运行结果:
com.lqh.singleton.LazySingleton@3d075dc0 com.lqh.singleton.LazySingleton@214c265e com.lqh.singleton.LazySingleton@448139f0 com.lqh.singleton.LazySingleton@7cca494b com.lqh.singleton.LazySingleton@7cca494b com.lqh.singleton.LazySingleton@448139f0 com.lqh.singleton.LazySingleton@448139f0 com.lqh.singleton.LazySingleton@448139f0 com.lqh.singleton.LazySingleton@448139f0 com.lqh.singleton.LazySingleton@448139f0 有不同的实例! 有不同的实例! 有不同的实例! 有不同的实例! 有不同的实例! ...
可以看到在多线程模式下就产生了多个不同的对象,为了避免这个情况就需要用到锁了,改进一下代码,只改动了getInstance()方法:
//代码段4:懒汉模式单例加锁 package com.lqh.singleton; /** * 单例模式-懒汉式 * @author lqh * @version 1.0 * @date: 2021/7/9 21:23 */ public class LazySingleton { /** * 在类内私有化一个静态对象,也就是唯一的实例,外面获取的实例都是指向这个对象 */ private static LazySingleton instance; /** * 构造方法私有化,防止在其他地方被实例化 * 此处虽然私有化了构造方法,但是还是能被反射获取到并调用 */ private LazySingleton(){} /** * 返回本类的实例对象,如果对象为空代表还没有被实例化, * 就需要调用构造方法实例化后返回 * @return */ public static LazySingleton getInstance(){ //在检测时加一层锁,锁住本类 synchronized(LazySingleton.class){ if (instance == null){ instance = new LazySingleton(); } return instance; } } }
再次运行单线程(代码段2)和多线程(代码段3)的代码:
单线程:
com.lqh.singleton.LazySingleton@1b6d3586 com.lqh.singleton.LazySingleton@1b6d3586 true com.lqh.singleton.LazySingleton@1b6d3586 com.lqh.singleton.LazySingleton@1b6d3586 ...
多线程:
com.lqh.singleton.LazySingleton@3d075dc0 com.lqh.singleton.LazySingleton@3d075dc0 com.lqh.singleton.LazySingleton@3d075dc0 ...
可以看到在加了一次锁之后在多线程模式下就又成功回到单例模式
由于以下内容暂时还无法理解所以先只是贴出有待再研究
package com.lqh.singleton; /** * 单例模式-懒汉式 * @author lqh * @version 1.0 * @date: 2021/7/9 21:23 */ public class LazySingleton { /** * 在类内私有化一个静态对象,也就是唯一的实例,外面获取的实例都是指向这个对象 * 新增volatile关键字防止指令重排(?何为指令重排) */ private static volatile LazySingleton instance; /** * 构造方法私有化,防止在其他地方被实例化 * 此处虽然私有化了构造方法,但是还是能被反射获取到并调用 */ private LazySingleton(){} /** * 返回本类的实例对象,如果对象为空代表还没有被实例化, * 就需要调用构造方法实例化后返回 * @return */ public static LazySingleton getInstance() { //第一层检查是否实例化对象 if (instance == null) { //加锁避免线程不安全 synchronized (LazySingleton.class) { //再次检查是否有实例化,没有就实例化(?为何二次检查) if (instance == null) { instance = new LazySingleton(); } } } //返回 return instance; } }
运行单线程(代码片段2)和多线程(代码片段3)的测试结果和多线程懒汉式单例的结果一样就不再次贴出