郴州网站建设公司官网网站建设费用低的公司
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:10
当前位置: 首页 > news >正文
郴州网站建设公司官网,网站建设费用低的公司,iis5.1 建立网站,企业公司简介范文目录
引言
单例模式
饿汉模式
懒汉模式
懒汉模式线程安全问题
分析原因 引言 设计模式为编写代码的 约定 和 规范 阅读下面文章前建议点击下方链接明白 对象 和 类对象
对象和类对象 单例模式 单个实例#xff08;对象#xff09;在某些场景中有特定的类#xff0c;…目录
引言
单例模式
饿汉模式
懒汉模式
懒汉模式线程安全问题
分析原因 引言 设计模式为编写代码的 约定 和 规范 阅读下面文章前建议点击下方链接明白 对象 和 类对象
对象和类对象 单例模式 单个实例对象在某些场景中有特定的类其只能被创建出一个实例不应该被创建多个实例而 单例模式 就针对上述的需求场景进行更强制的保证通过巧用 java 的现有语法实现了某个类只能被创建出一个实例的效果从而当程序员不小心创建出多个实例时会编译报错 实例理解 JDBC 编程中的 DataSource 类我们仅连接一个数据库时DataSource 类描述数据的来源用于获取数据库连接因为数据只来源于一个数据库所以我们仅创建一个实例即可无需创建多个实例从而该场景适合使用单例模式 具体思路 static 关键字可以将成员变量或方法声明为静态的让它们属于类级别而不是实例级别因此我们可以将 单例对象 声明为静态变量让其可以在任何地方直接通过类名来访问无需创建类的实例从而这样便可以方便地获取单例对象并且对于多个调用者来说始终返回同一个实例 简单理解 static 关键字将 单例对象 转为 静态变量因此 单例对象 便从 与实例相关联 转为 与类相关联又因为 在一个 java 进程中对于同一个类只会存在一个对应的类对象所以该 类对象内部的类属性也仅会存在一份也就是作为类属性的 单例对象 也仅会存在一份 饿汉模式 // 饿汉模式的 单例模式 实现
// 此处保证 Singleton 这个类只能创建出一个实例
class Singleton {
// 在此处先把这个实例给创建出来了
// 使用 private 修饰是为了防止在类外对 Singleton 实例 instance 进行修改private static Singleton instance new Singleton();// 如果需要使用这个唯一实例统一通过 Singleton.getInstance() 方式来获取public static Singleton getInstance() {return instance;}// 为了避免 Singleton 类不小心被复制出来多份
// 把构造方法设为 private 在类外面就无法通过 new 的方式来创建这个 Singleton 实例了private Singleton() {}
}public class ThreadDemo19 {public static void main(String[] args) {Singleton s Singleton.getInstance();Singleton s2 Singleton.getInstance();System.out.println(s s2);}
} 该模式表示在类加载阶段就已经把实例创建出来了所以 饿汉 一词便体现出创建该实例的急迫感 懒汉模式 class SingletonLazy {private static SingletonLazy instance null;public static SingletonLazy getInstance() {if(instance null) {instance new SingletonLazy();}return instance;}private SingletonLazy(){}
}public class ThreadDemo20 {public static void main(String[] args) {SingletonLazy s1 SingletonLazy.getInstance();SingletonLazy s2 SingletonLazy.getInstance();System.out.println(s1 s2);}
} 该模式在创建实例时并非是在类加载阶段就已经把实例创建出来了而是当真正第一次使用的时候才创建实例所以相比于 饿汉 模式创建实例的急切感懒汉 模式则显得没那么着急 阅读下面文章之前建议点击下方链接了解清楚线程安全问题
线程安全问题详解 懒汉模式线程安全问题 相比于 饿汉模式 仅涉及到读操作懒汉模式 则既涉及到 写操作 又涉及到 读操作 显然 懒汉模式 有着线程安全问题 分析原因 懒汉模式线程安全问题的本质为 读操作、比较操作、写操作 这三个操作并不是原子的从而便会导致线程t2 读到的 instance 值可能是线程t1 还没来得及写的这也就是我们常说的 脏读 此时我们便可以利用 synchronized 关键字来进行加锁使得上图中的指令变为原子的 public static SingletonLazy getInstance() {synchronized (SingletonLazy.class) {if(instance null) {instance new SingletonLazy();}}return instance;} 加锁的对象是 SingletonLazy.class 类对象该锁是基于类的 虽然对 SingletonLazy.class 类对象进行加锁能解决多线程之间脏读的问题但是也导致了每次调用 getInstance 方法时都需要先进行加锁才能进入方法内部进行判断 instance 是否为空非空则触发 return 直接返回单例对象我们要清楚的一点是 加锁操的开销还挺大会涉及到用户态到内核态之间的切换这样切换成本的成本是很高的要注意到的是 在 new 完单例对象之后后续再调用 getInstance 方法时我们仅会直接返回单例对象即仅涉及到读操作这是没有线程安全问题的所以在 new 出对象之前有加锁操作这是十分有必要的即任意线程第一次调用getInstance 方法 在 new 完单例对象之后我们无需再进行加锁操作这样便可以很大程度上提高效率 public static SingletonLazy getInstance() {if (instance null){synchronized (SingletonLazy.class) {if(instance null) {instance new SingletonLazy();}}}return instance;} 我们便可以在 加锁操作 的外层再加上个 if 判断判断 instance 对象是否已经被创建出来了从而该代码只会在任意线程第一次调用 getInstance 方法时才会进行加锁操作从而此处不再是无脑加锁而是满足了特定条件之后才真正加锁 我们需要理解此处为什么会有两个相同的 if 判断首先如果这两个 if 判断之间没有加锁操作那么写两个一模一样 if 判断是毫无意义的但是正因为这两个 if 判断之间有加锁操作而加锁操作就可能会引起线程阻塞当线程竞争到锁之后再执行到第二个 if 判断的时候可能与第一次执行 if 判断之前隔了很长一段时间 举例理解 线程A 第一次调用 getInstance 方法读取到 instance 为 null通过第一次 if 判定并成功为锁对象进行加锁操作然后再次读取到 instance 为 null通过第二次 if 判定进而直接 new 出一个 instance 对象最后再将锁释放可能线程B 比线程A 晚一点点的调用了 getInstance 方法可能此时线程A 并未修改完instance 的值从而线程B 读取到 instance 为 null通过了第一次 if 判定然后进行阻塞等待线程A 释放锁但正是在线程B 等待锁的在这段时间里线程A 已经将 instance 对象给创建出来了此时线程B 再获取到锁时instance 的值已经发生改变了线程B 再次读取 instance 的值此时 instance 不为 null从而未通过第二次 if 判断直接返回 instance 的值这就意味着第二次 if 判断成功阻止了线程B 再创建一个新的 instance 对象根据上述例子深入理解 图中第一个 if 负责判定是否要加锁解决了每次调用getInstance 方法时都需要引入无意义的加锁操作很大程度上减少了开销第二个 if 负责判定是否要创建对象是最初为了保障单例模式引入的必要条件这两 if 判断的目的是完全不相同的只是碰巧代码是一样的 上述仅解决了多线程之间 脏读 的问题但是还可能会有 内存可见性问题假设有很多线程都去执行 getInstance 方法这个时候便可能存在被优化的风险即只有第一次读才是真正读了内存后续都是读寄存器或 cache 同时还可能涉及到 指令重排序问题编译器为了提高程序的效率调整代码执行顺序即 我们可以将 instance new Singleton()拆分为三个步骤步骤 1申请内存空间步骤 2调用构造方法把这个内存空间初始化成一个合理的对象步骤 3把内存空间的地址赋值给 instance 引用编译器可能将步骤的执行顺序由 1、2、3优化重排序为 1、3、2如果仅是在单线程场景下执行步骤的调换是没有任何影响的但是如果是在多线程环境下我们举一个简单例子来理解指令重排序所带来的问题 举例理解 假设编译器优化指令重排序线程A 的步骤执行顺序变为 1、3、2如果线程A 执行完步骤 1、3正当要执行步骤 2 时被切出 CPUCPU 调度执行线程B我们要注意到的是此时线程A 执行完步骤 1、3 后会创建出一个非法对象即该对象仅分配了内存其数据是无效的只有执行完步骤 2 才会把这个内存空间初始化成一个合理的对象那么当 CPU 调度执行线程B 时线程B 又正好调用 getInstance 方法此刻便会进入第一个 if 判断获取 instance 对象的值来判断是否为 null因为 instance 对象 已经被分配好了内存空间所以线程B 获取到的 instance 对象值并不会为 null所以线程B 将会直接返回该 instance 对象注意此处线程B 返回的 instance 对象 是上述讲的非法对象即仅分配了内存其数据是无效的所以之后 线程B 拿着这个非法对象来进行使用便将会出现许多问题和错误 解决方法 引入 volatile 关键字volatile 关键字的功能正好能解决 内存可见性 和 指令重排序 class SingletonLazy {private volatile static SingletonLazy instance null;public static SingletonLazy getInstance() {if (instance null){synchronized (SingletonLazy.class) {if(instance null) {instance new SingletonLazy();}}}return instance;}private SingletonLazy(){}
}public class ThreadDemo20 {public static void main(String[] args) {SingletonLazy s1 SingletonLazy.getInstance();SingletonLazy s2 SingletonLazy.getInstance();System.out.println(s1 s2);}
} 以上完整的代码便是 线程安全的懒汉模式 完全体
- 上一篇: 郴州网站策划百度网站入口链接
- 下一篇: 郴州网站建设公司在哪里牛商的网站后台
相关文章
-
郴州网站策划百度网站入口链接
郴州网站策划百度网站入口链接
- 技术栈
- 2026年03月21日
-
郴州网站seo优化装潢设计师工资一般多少
郴州网站seo优化装潢设计师工资一般多少
- 技术栈
- 2026年03月21日
-
郴州 网站建设网络规划设计师教程第二版
郴州 网站建设网络规划设计师教程第二版
- 技术栈
- 2026年03月21日
-
郴州网站建设公司在哪里牛商的网站后台
郴州网站建设公司在哪里牛商的网站后台
- 技术栈
- 2026年03月21日
-
郴州网站建设哪个好百度一下知道首页
郴州网站建设哪个好百度一下知道首页
- 技术栈
- 2026年03月21日
-
郴州网站建设有限公司辽宁省住房与城乡建设厅网站
郴州网站建设有限公司辽宁省住房与城乡建设厅网站
- 技术栈
- 2026年03月21日






