01 设计模式-单例模式
单例模式
饿汉式
package com.hxh;
public class Hungry {
private static final Hungry HUNGRY = new Hungry();
private Hungry() {
System.out.println("创建");
}
public static Hungry getInstance() {
return HUNGRY;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Hungry.getInstance());
}).start();
}
}
}
注意事项:
- 变量HUNGRY必须是:私有,静态,不可变;
- 构造器私有;
- 公开的
getInstance
方法,使外界可以获取变量。
懒汉式
package com.hxh;
public class LazyMan {
private static LazyMan lazyMan;
private LazyMan() {
System.out.println("LazyMan被创建");
}
public static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println(LazyMan.getInstance());
}
}
}
输出如下,单线程下看起来毫无问题:
LazyMan被创建 com.hxh.LazyMan@39a054a5 com.hxh.LazyMan@39a054a5 com.hxh.LazyMan@39a054a5 com.hxh.LazyMan@39a054a5 com.hxh.LazyMan@39a054a5 com.hxh.LazyMan@39a054a5 com.hxh.LazyMan@39a054a5 com.hxh.LazyMan@39a054a5 com.hxh.LazyMan@39a054a5 com.hxh.LazyMan@39a054a5
修改main函数,改为多线程
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(LazyMan.getInstance());
}).start();
}
}
从上图可以看出,一共创建了4个对象,并不是单例的。
通过修改获取实例方法getInstance
,给对象加锁解决上面的问题:
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
输出如下:
LazyMan被创建 com.hxh.LazyMan@33ca79ec com.hxh.LazyMan@33ca79ec com.hxh.LazyMan@33ca79ec com.hxh.LazyMan@33ca79ec com.hxh.LazyMan@33ca79ec com.hxh.LazyMan@33ca79ec com.hxh.LazyMan@33ca79ec com.hxh.LazyMan@33ca79ec com.hxh.LazyMan@33ca79ec com.hxh.LazyMan@33ca79ec
现在看起来好像并没有什么问题。但是lazyMan = new LazyMan();
这并不是一个原子性操作,极端情况下可能会发生指令重排,那么什么是指令重排呢?
指令重排:
- JVM创建对象分为三步:
- 分配内存空间
- 执行构造方法、创建实例
- 将内存与实例关联
- 问题就出现在上面的步骤中,我们所预期的步骤是123,而编译器有时为了优化性能,会选择其他的顺序执行上面的步骤。在单线程下没有影响,而多线程时就会出现问题,如:
- 线程a执行步骤为132;
- 线程b执行步骤为123;
- 在线程a执行到3时,b线程开始执行,发现对象不为空之后就开始执行下面的语句,可能就会出现异常,因为a线程还未将对象初始化成功。
解决办法也很简单,为lazyMan
对象添加volatitle
关键字,保证不会出现指令重排
完整代码如下:
package com.hxh;
public class LazyMan {
private volatile static LazyMan lazyMan;
private LazyMan() {
System.out.println("LazyMan被创建");
}
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(LazyMan.getInstance());
}).start();
}
}
}
- 使用R语言挖掘QQ群聊天记录
- 解析滴滴算法大赛---GBDT进行数据预测
- 数据迁移中的数据库检查和建议(r2笔记71天)
- 决策树案例:基于python的商品购买能力预测系统
- 数据迁移前的准备和系统检查 (r2笔记70天)
- 数据处理的统计学习(scikit-learn教程)
- 机器学习实战,使用朴素贝叶斯来做情感分析
- Python NLTK 处理原始文本
- 通过闪回事务查看数据dml的情况 (r2笔记69天)
- 通过shell和sql结合查找性能sql(r2笔记68天)
- 淘宝的评论归纳是用什么方法做到的?
- Python的机器学习实战:AadBoost
- 通过shell检查分区表中是否含有默认分区(r2笔记87天)
- 利用python爬取人人贷网的数据
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- npm相关操作
- Angular html property的值如何被更新的
- Centos 7 使用mailx发送邮件
- Centos 7 备份MySQL
- Centos 7 备份MySQL/MongoDB并发邮件脚本
- Nginx启动报错:sbin/nginx: error while loading shared libraries: libpcre.so.1: cannot open shared object
- 有赞美业店铺装修前端解决方案
- nginx部署react项目
- Golang 标准库 限流器 time/rate 设计与实现
- Node搭建简易的UDP服务器
- cordova run android 下载gradle报timeout
- How we redesign the NSQ-NSQ重塑之客户端
- [PHP] 使用php生成下载csv文件
- 深入浅出MySQL crash safe
- Cordova+React+Ant.design项目搭建