在西宁做网站可以吗,wordpress官方模版,奔驰高端品牌,郑州seo顾问阿亮1. 单例模式概念
设计模式#xff1a;谈到单例模式#xff0c;我们首先需要知道什么是设计模式#xff0c;设计模式是软件工程中的一大重要概念#xff0c;是被广泛认可并使用于解决特定实际问题的代码设计经验#xff0c;校招中常考的设计模式有单例模式、工厂模式 等谈到单例模式我们首先需要知道什么是设计模式设计模式是软件工程中的一大重要概念是被广泛认可并使用于解决特定实际问题的代码设计经验校招中常考的设计模式有单例模式、工厂模式 等而我们需要重点掌握单例模式代码的编写 简单来说设计模式就是大佬们为了不让我们这些小菜鸟写烂代码而总结出来的代码编写方式 单例模式单例模式要求类在一个Java进程只能拥有唯一一个实例而无法创建出多个实例尝试使用new关键字创建多个实例的时候就会报错
2. 单例模式代码示例
简单介绍了设计模式以及单例模式的相关概念光说不练假把式现在我们就来尝试编写单例模式的代码在正式编写代码之前我们需要知道单例模式有两种实现方式1、饿汉模式2、懒汉模式二者之间的差异就在于创建唯一实例的时机不同
2.1 饿汉模式
2.1.1 代码案例
/*** 单例模式类*/
class Singleton {private static Singleton instance new Singleton(); // 指向唯一实例public static Singleton getInstance() {return instance;}private Singleton() {};}public class SingletonDemo01 {public static void main(String[] args) {Singleton instance1 Singleton.getInstance();Singleton instance2 Singleton.getInstance();System.out.println(instance1 instance2);}
}运行结果如图所示
2.1.2 代码分析
首先观察唯一实例的创建时机private static Singleton instance new Singleton();该实例在类加载的时候就创建完毕因此称为饿汉式再来观察单例模式中的构造方法普通类的构造方法一般是public修饰的但是单例模式中将构造方法私有化使用private修饰因此外界尝试使用new关键字创建实例就会报错单例模式提供了一个public static修饰的获取唯一实例的方法外界只能通过调用这个方法来获取该类的唯一实例这样就实现了该类只能拥有唯一实例的要求 我相信有的小伙伴一定此时忍不住发出疑问如果使用反射机制那不就可以创建出多个实例了嘛事实上如此但是毕竟反射不属于常规方法毕竟在代码中滥用反射是非常不好的行为 2.2 懒汉模式
2.2.1 代码案例
/*** 懒汉式单例模式*/
class SingletonLazy {private static SingletonLazy instance null; // 唯一实例public static SingletonLazy getInstance() {if (instance null) {instance new SingletonLazy();}return instance;}private SingletonLazy() {};}2.2.2 代码分析
懒汉模式的唯一实例并不是在类加载的时候就创建完毕而是遵循能不创建就不创建能晚创建就晚创建的原则直到外界有程序调用getInstance方法获取实例时才创建完毕这就是懒汉式的由来懒汉模式与饿汉模式一致都将构造方法设置成私有懒汉模式与饿汉模式一致都提供getInstance方法供外界获取该类的唯一实例 注在计算机世界中懒往往意味着效率高考虑这样一个场景有一个10GB的文件你使用文本编辑器打开如果采用饿汉式的方式系统会将10GB的文件一次性加载到内存中然后统一展示然而如果使用懒汉式方式打开则编辑器先加载10KB文件让用户阅读随着用户进行翻页操作再继续加载10KB文件到内存中。此时懒汉式无疑效率更高 3. 单例模式的线程安全问题
3.1 问题引入
上述关于单例模式的介绍只是序幕毕竟我们本章重点论述的还是多线程主题下面就有一个重要问题了上述我们编写的单例模式代码是否存在 线程安全问题 呢
饿汉模式由于饿汉模式中创建唯一实例的时机在类加载的时候而调用getInstance方法执行的过程中直接返回该唯一实例是纯粹的读取操作不涉及多个线程修改同一变量的情况因此天然就是线程安全的懒汉模式想必聪明的小伙伴已经想到了懒汉模式是不是就是线程不安全的呢就是这样下面我们就来考虑多个线程同时执行的情况
演示懒汉模式的线程安全问题 如图所示线程t1首先进入条件判断当前实例为null然后进入if语句块中但是此时还没有来得及执行new操作创建实例就被调度出CPU此时线程t2进行判断当前实例为null然后进入if语句块中创建实例完成返回此时线程t1重新被CPU调度接着执行new SingletonLazy()方法因此创建出了多个实例即懒汉式单例模式线程不安全
3.2 解决懒汉式线程安全问题
那么如何改进上述代码让懒汉式单例模式变成线程安全的呢出现问题的关键在于if条件判断和new创建实例并不是原子操作因此解决方法就是通过synchronized关键字将这两个操作打包成原子操作
3.2.1 改进代码解决线程安全问题
/*** 改进版本单例模式解决线程安全问题*/
class SingletonLazyImprove01 {private static SingletonLazyImprove01 instance null; // 唯一实例private static Object locker new Object();public static SingletonLazyImprove01 getInstance() {synchronized (locker) {if (instance null) {instance new SingletonLazyImprove01();}}return instance;}private SingletonLazyImprove01() {};
}上述代码我们引入synchronized关键字将if条件判断与new创建实例操作加锁打包成原子操作此时如果继续按照刚才的场景线程t1先进行加锁如果t2也尝试进入if语句块就会因为锁竞争而阻塞等待直到线程t1创建完实例之后释放锁此时t2线程判断实例已经被创建好就直接返回因此不会出现线程安全问题了
3.2.2 改进代码解决效率问题
但是问题还没有结束现在的代码虽然解决了线程安全问题但是还存在着效率问题因为我们这里调用getInstance都会先尝试加锁然后判断当前实例是否为空然后再解锁但是线程安全问题只存在于第一次尝试创建实例的时候如果实例已经创建完毕后续所有的操作都是读取操作就不会再产生线程安全问题了所以我们引入的解决方式就是 if双重校验锁 其代码如下
class SingletonLazyImprove02 {private static SingletonLazyImprove02 instance null;private static Object locker new Object();public static SingletonLazyImprove02 getInstance() {if (instance null) {synchronized (locker) {if (instance null) {instance new SingletonLazyImprove02();}}}return instance;}private SingletonLazyImprove02() {};
}此时我们会看到有两个if判断语句换做是单线程情况下我们从来不会写这样的代码但是一旦涉及到多线程的时候if双重校验锁是很常见的其中第一个if语句用来判断是否需要进行加锁操作第二个if语句用来判断是否需要创建实例但是恰巧两个if语句的条件一样
3.2.3 改进代码解决指令重排序问题
但是上述代码还有一些小问题我们需要先介绍有关 指令重排序 的话题
指令重排序指令重排序是一种编译器优化手段会在保证逻辑不变的情况下调整原有代码的执行顺序来提高程序的效率但是有可能会引发线程安全问题 例如在创建实例的代码中instance new SingletonLazyImprove02();内部包括三个核心步骤
申请一块内存空间调用构造方法对内存空间进行初始化将内存空间地址赋值给instance变量
正常情况下执行顺序都是按照1-2-3进行的但是编译器也可能会优化为1-3-2的步骤来执行这两种在单线程环境下都没有问题但是如果在多线程情况下执行就有可能出现线程安全问题 如图所示线程t1先进行加锁然后创建实例过程中先执行第一步与第三步此时被调度出CPU但是后来的线程判断实例是否为空此时直接返回未被初始化的内存空间地址这时就会出现错误
volatile关键字我们在之前的章节已经提到过使用volatile关键字可以解决内存可见性和指令重排序的问题因此该问题的解决方式就是使用volatile关键字强制让编译器完全按照1-2-3的顺序来执行
/*** 懒汉式单例模式改进版本解决指令重排序*/
class SingletonLazyImprove03 {private static volatile SingletonLazyImprove03 instance null;private static Object locker new Object();public static SingletonLazyImprove03 getInstance() {if (instance null) {synchronized (locker) {if (instance null) {instance new SingletonLazyImprove03();}}}return instance;}private SingletonLazyImprove03() {};
}