从单例谈double-check必要性,多种单例各取所需
2024-09-07 13:44:12
theme: fancy
前言
- 前面铺掉了那么多都是在讲原则,讲图例。很多同学可能都觉得和设计模式不是很搭边。虽说设计模式也是理论的东西,但是设计原则可能对我们理解而言更加的抽象。不过好在原则东西不是很多,后面我们就可以开始转讲设计模式了。
- 我的思路是按照设计模式进行分类整理。期间穿插相关的知识进行扩展从而保证我们学习的更加的全面。在正式开始前我现在这里立个Flag。争取在20周内完成我们设计模式章节的内容。期间可能会有别的学习,20周争取吧
- 相信单例模式是大家第一个使用到的设计模式吧。不管你怎么样,我第一个使用的就是单例模式。其实单例模式也是分很多种的【饿汉式】、【懒汉式】。如果在细分还有线程安全和线程不安全版本的。
饿汉式
- 顾名思义饿汉式就是对类需求很迫切。从Java角度看就是类随着JVM启动就开始创建,不管你是否使用到只要JVM启动就会创建。
public class SingleFactory
{
private static Person person = new Person();
private SingleFactory()
{
}
public static Person getInstance()
{
return person;
}
}
- 上面这段代码就是饿汉式单例模式。通过这单代码我们也能够总结出单例模式的几个特点
特点 隐藏类的创建即外部无法进行创建 内部初始化好一个完整的类 提供一个可以访问到内部实例的方法,这里指的是getInstance
- 单例模式特点还是很容易区分的。饿汉式感觉挺好的,那为什么后面还会出现懒汉式及其相关的变形呢?下面我们就来看看饿汉式有啥缺点吧。
- 首先上面我们提到饿汉式的标志性特点就是随着JVM 的启动开始生成实例对象。这是优点同时也是缺点。大家应该都用过Mybatis等框架,这些框架为了加快我们程序的启动速度纷纷推出各种懒加载机制。
- 何为懒加载呢?就是用到的时候再去初始化相关业务,将和启动不相关的部分抽离出去,这样启动速度自然就快了起来了。在回到饿汉式,你不管三七二十一就把我给创建了这无疑影响了我的程序启动速度。如果这个单例模式你使用了倒还好,假如启动之后压根就没用到这个单例模式的类,那我岂不是吃力不讨好。不仅浪费了时间还浪费了我的空间。
- 所以说,处于对性能的考虑呢?还是建议大家不要使用饿汉式单例。但是,存在即是合理的,我们不能一棒子打死一堆人。具体场景具体对待吧XDM。
变形1
public class SingleFactory
{
private static Person person ;
static {
person = new Person();
}
private SingleFactory()
{
}
public static Person getInstance()
{
return person;
}
}
- 咋一看好像和上面的没啥区别哦。仔细对比你就会发现我们这里并没有立刻创建Person这个类,而是放在静态代码块中初始化实例了。
- 放在静态代码块和直接创建其实是一样的。都是通过类加载的方式来进行实例化的。基本同根同源没啥可说的 。
- 关于Static关键字我们之前也有说过,他涉及到的是类加载的顺序。我们在类加载的最后阶段就是执行我们的静态代码块
懒汉式
public class SingleFactory
{
private static Person person = null;
private SingleFactory()
{
}
public static Person getInstance()
{
try
{
Thread.sleep(30);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
if(person==null){
person=new Person();
}
return person;
}
}
- 懒汉式就是将我们的对象创建放在最后一刻进行创建。并不是跟随类加载的时候生成对象的,这样会造成一定程度的内存浪费。懒汉式更加的提高了内存的有效利用。在
getInstance
方法中我们在获取对象前判断是否已经生成过对象。如果没有在生成对象。这种行为俗称懒,所以叫做懒汉式单例模式
变形1
- 上面懒汉式单例中我加入了睡眠操作。这是因为我想模拟出他的缺点。上面这种方式在高并发的场景下并不能保证系统中仅有一个实例对象。
public class SingleFactory
{
private static Person person = null;
private SingleFactory()
{
}
public static Person getIstance()
{
try
{
Thread.sleep(30);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
synchronized (SingleFactory.class)
{
if (person == null)
{
person = new Person();
}
}
return person;
}
}
- 只需要加一把锁,就能保证线性操作了。但是仔细想想难道这样就真的安全了吗。
double-check
- 在多线程下安全的单例模式应该非double-check莫属了吧。
public class OnFactory {
private static volatile OnFactory onFactory;
public static OnFactory getInstance() {
if (null == onFactory) {
synchronized (OnFactory.class) {
if (null == onFactory) {
onFactory = new OnFactory();
}
}
}
return onFactory;
}
}
- 这段代码是之前咱们学习double-check和volatile的时候写过的一段代码。在这里我们不仅在锁前后都判断了而且还加上了volatile进行内存刷新。关于volatile需要的在主页中搜索关键词即可找到。这里仅需要知道一点volatile必须存在否则线程不安全。
最新文章
- Oracle_创建和管理表
- linux命令:文件属性
- opengl
- 每天学点GDB 12
- 高通平台 lcd driver 调试小结
- 三星Galaxy S4(GT-I9500)获取ROOT权限教程(转)
- lightoj 1013 dp
- 关于hibernate非主键多对一关联
- C#共享内存类改进版
- JSON小结
- PHP冒泡排序、选择排序、插入排序
- Angular2+ 编译后部署到服务器上页面刷新404问题
- JavaScript 神奇的参数
- Go 安装 sqlite3驱动报错
- 翻译1-在SQL Server 2016中介绍微软R服务
- A little issue in Mathematical Thought from Ancient to Modern Times, Vol. 3
- 文件缓冲区在fork后复制
- C语言调用汇编
- ARC075 E.Meaningful Mean(树状数组)
- SGDMA
热门文章
- .NET程序设计实验一
- flex布局控制最后一个元素右浮动
- Java基础之浅谈异常与了解断言
- MySQL 8 数据源配置
- 4.Docker容器学习之Dockerfile入门到放弃
- 3.Docker容器学习之新手基础使用
- Java学习day5
- Java实现负载均衡算法--轮询和加权轮询
- 使用Lua 脚本实现redis 分布式锁,报错:ERR Error running script (call to f_8ea1e266485534d17ddba5af05c1b61273c30467): @user_script:10: @user_script: 10: Lua redis() command arguments must be strings or integers .
- 不care工具,在大数据平台中Hive能自动处理SQL