java单例模式是什么意思,JAVA单例模式
前言
本文来自我的微信官方账号。如果没看过,直接看就好了。如果你读过,你可以再读一遍。我稍微修改了一些内容。今天的解释如下:
一、什么是单例模式
【singleton pattern】,英文名:Singleton Pattern,这种模式非常简单,一个类型只需要一个实例,是属于创建类型的常见软件设计模式。singleton pattern的方法创建的类在当前进程中只有一个实例(如果需要,也可能属于一个线程中的单个实例,例如,在线程上下文中只使用同一个实例)。
(推荐视频:java视频教程)
我们大概知道,其实说白了,整个项目周期只会有一个实例。当项目停止时,实例将被销毁,当它重新启动时,我们的实例将再次产生。
上面的文章里提到了一个名词【创造型】的设计模式。创作型的设计模式是什么?
创建型(Creational)模式:负责对象创建,我们使用这个模式,就是为了创建我们需要的对象实例的。
除了创造性的模式,还有两种其他类型的模式:
这两种设计模式将在后面讨论。这里,先按“不看”键。
让我们关注如何从0创建singleton模式的对象实例。
二、如何创建单例模式
实现单例模式的方法有很多:从“懒汉式”到“饿汉式”,最后“双检锁”模式,。这里我们将一步一步地解释如何创建单例。
1、正常的思维逻辑顺序
因为我们想要创建一个单一的实例,我们首先需要学习如何创建一个实例。这个很简单。我相信每个人都会创建一个实例,就像这样:
///摘要
///定义天气类
////摘要
公共类天气预报
{
公共天气预报()
{
日期=日期时间。现在;
}
公共日期时间日期{ get设置;}
公共int TemperatureC { get设置;}
public int temperature ef=32(int)(temperature c/0.5556);
公共字符串摘要{ get设置;}
}
[HttpGet]
公共天气预报获取()
{
//实例化一个对象实例
WeatherForecast天气=new weather forecast();
返程天气;
}我们每次访问,时间都会发生变化,所以我们的实例总是在创建和变化:
相信大家都能看出来这个代码是什么意思。话不多说,直接下去。我们知道单例模式的核心目的是:
必须保证这个实例在整个系统的运行周期内是唯一的,这样可以保证中间不会出现问题。
好吧,让我们改进它。我们不是说要唯一的吗?说吧!为什么我不回去呢?
///摘要
///定义天气类
////摘要
公共类天气预报
{
//定义一个静态变量来保存该类的唯一实例
私有静态天气预报uniqueInstance
//定义一个私有构造函数,让外界无法创建这个类的实例。
私人天气预报()
{
日期=日期时间。现在;
}
///摘要
///静态方法,以返回唯一实例
///如果存在,则返回
////摘要
///返回/返回
公共静态天气预报GetInstance()
{
//如果该类的实例不存在,则创建一个,否则直接返回
//其实严格来说这不属于[singleton]
if (uniqueInstance==null)
{
unique instance=new weather forecast();
}
返回uniqueInstance
}
公共日期时间日期{ get设置;} public int TemperatureC { get设置;}
public int temperature ef=32(int)(temperature c/0.5556);
公共字符串摘要{ get设置;}
}然后我们修改调用方法,因为我们的默认构造函数已经私有化,不允许再创建实例,所以我们就这样调用它:
[HttpGet]
公共天气预报获取()
{
//实例化一个对象实例
天气预报天气=天气预报。GetInstance();
返程天气;
}最后,我们来看看效果:
这时,我们可以看到时间没有改变,也就是说,我们的实例是唯一的,我们完成了!你很开心吗?
但是,不用担心,问题来了。我们目前是单线程的,所以只有一个。多线程呢?如果多个线程同时访问,也会正常吗?
我们在这里做个测试。当项目启动时,我们使用多线程来调用它:
[HttpGet]
公共天气预报获取()
{
//实例化一个对象实例
//WeatherForecast天气=天气预报。GetInstance();
//要调用的多线程
for(int I=0;i3;我)
{
var th=新线程(
新的parameterized threadstart((state)=1
{
天气预报。GetInstance();
})
);
th。开始(一);
}
返回null
}那我们来看看效果如何。按照我们的思路,应该是构造者只会走一次,其实不是:
三个线程第一次访问GetInstance方法时,同时判断(uniqueInstance==null)这个条件时都返回true,然后都创建实例。这肯定是不对的。那我们能做什么?只要让GetInstance方法只运行一个线程,我们就可以加一个锁来控制它。代码如下:
公共类天气预报
{
//定义一个静态变量来保存该类的唯一实例
私有静态天气预报uniqueInstance
//定义一个锁来防止多线程
私有静态只读对象locker=new object();
//定义一个私有构造函数,让外界无法创建这个类的实例。
私人天气预报()
{
日期=日期时间。现在;
}
///摘要
///静态方法,以返回唯一实例
///如果存在,则返回
////摘要
///返回/返回
公共静态天气预报GetInstance()
{
//当第一个线程执行时,它将“锁定”locker对象,
//其他线程执行时,会等待locker完成解锁。
锁(储物柜)
{
//如果该类的实例不存在,则创建一个,否则直接返回
if (uniqueInstance==null)
{
unique instance=new weather forecast();
}
}
返回uniqueInstance
}
公共日期时间日期{ get设置;}
公共int TemperatureC { get设置;}
public int temperature ef=32(int)(temperature c/0.5556);
公共字符串摘要{ get设置;}
}这时我们进行并发测试,发现都是一样的,从而达到了想要的效果。但这真的是最完美的吗?其实不是的。因为我们锁了锁,所以只判断第一次是不是空的。如果锁被创建,我们以后就不用担心它了。我们只关心uniqueInstance是否为空。那么让我们完善它:
///摘要
///定义天气类
////摘要
公共类天气预报
{
//定义一个静态变量来保存该类的唯一实例
私有静态天气预报uniqueInstance
//定义一个锁来防止多线程
私有静态只读对象locker=new object();
//定义一个私有构造函数,让外界无法创建这个类的实例。
私人天气预报()
{
日期=日期时间。现在;
}
///摘要
///静态方法,以返回唯一实例
///如果存在,则返回
////摘要
///返回/返回
公共静态天气预报GetInstance()
{
//当第一个线程执行时,它将“锁定”locker对象,
//其他线程执行时,会等待locker完成解锁。
if (uniqueInstance==null)
{
锁(储物柜)
{
//如果该类的实例不存在,则创建一个,否则直接返回
if (uniqueInstance==null)
{
unique instance=new weather forecast();
}
}
}
返回uniqueInstance
}
公共日期时间日期{ get设置;}
公共int TemperatureC { get设置;}
public int temperature ef=32(int)(temperature c/0.5556);
公共字符串摘要{ get设置;}
}只有这样,我们才能最终完美地实现我们的单例模式!完成了。
2、幽灵事件:指令重排
当然,如果你看完了以上四个步骤,就已经可以开始了。通常,这是我们使用和思考的,但它真的是万无一失的。有个JAVA朋友提了这个问题,C#里没听说过。我无知吗:
单例模式的幽灵事件,时令重排会偶尔导致单例模式失效。
是不是听起来很高大上,很迷茫?没关系。我们通常不使用它,但我们可以理解:
这是从网上摘录的。只看大概意思。理解双止锁失效的原因有两个关键点。
1.编译器的写重排问题。
例:B B=new B();
上面这句话不是原子操作。一部分是新建一个B对象,一部分是将新对象赋给B .
直观上,我们可能认为我们先构造对象,然后赋值。可惜这个顺序不固定。在编译器的重排下,可能会出现先赋值再构造对象的情况。
2.结合上下文和使用场景。
在了解了1中写操作的重排后,我卡住了,因为我真的不知道这个重排会有什么影响。其实是因为我看代码不够仔细,没有意识到使用场景。双重检查锁的一个常见使用场景是在singleton模式下初始化一个singleton并返回,然后在初始化方法的方法体中使用初始化的singleton对象。
三、Singleton = 单例 ?
上面说了很多,也介绍了Singleton的原理和步骤。那么问题来了。当我们在学习依赖注入的时候,Singleton singleton注入和上面说的用的是一个东西吗?这里,我们直接用多线程测试一下:
///摘要
///定义一个情绪类
////摘要
公众阶级感觉
{
公众感受()
{
日期=日期时间。现在;
}
公共日期时间日期{ get设置;}
}
//单个实例注册到容器中。
服务。AddSingletonFeeling();然后我们将服务注入控制器,然后进行多线程测试:
private readonly ILoggerWeatherForecastController _ logger;
私人只读Feeling _ feeling
公共WeatherForecastController(ILoggerWeatherForecastController记录器,感觉感觉)
{
_ logger=logger
_feeling=感觉;
}
[HttpGet]
公共天气预报获取()
{
//实例化一个对象实例
//WeatherForecast天气=天气预报。GetInstance();
//要调用的多线程
for(int I=0;i3;我)
{
var th=新线程(
新的parameterized threadstart((state)=1
{
//天气预报。GetInstance();
//此刻的心情
控制台。WriteLine(_感觉。日期);
})
);
th。开始(一);
}
返回null
}测试的结果,合理,我们项目初始化服务的时候只进了一次构造函数:
和我们上面说的一样,Singleton是一种单例,也是双检锁类型,因为结论表明,我们使用singleton模式时,可以直接使用依赖注入Sigleton来满足,非常方便。
四、单例模式的优缺点
单例模式的[卓越]优势:
(1)保证唯一性:防止其他对象实例化,保证实例的唯一性;
(2)整体:数据定义后,当前实例和数据可以在整个项目的任何地方使用;
[不好],单例模式的缺点:
(1)内存常驻:由于单个实例的生命周期最长,存在于整个开发系统中,如果一直添加数据或者常驻,会造成一定的内存消耗。
以下内容来自百度百科:
本文来自我们,java教程专栏,欢迎学习!这就是java Singleton模式和Singleton的细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。