当前位置:网站首页>并发编程-单例
并发编程-单例
2022-07-03 10:44:00 【戴着假发的程序员】
并发编程-单例
说在前面的话
今天的文章很短,但是很经典,值得你仔细阅读每一个文字…
正如我开篇所说,我们要整理一些java并发编程的学习文档,这一篇就是第八篇:单例模式。这一篇主要聊聊单例的几种实现方式已经适用的场景。
开整
稍微理解一下
专业解释:单例就是确保一个类只有一个实例,并且有一个全局获取这个实例的访问点。
简单的说呢就是一个类只能创建一个实例,任何时候使用这个类的实例对象都是同一个。
基本都是了减少这个类对象创建和销毁过程的消耗。
嗯!
思考思考,一个类如果只有一个实例,必然不能随便创建,所以单例类的关键代码就是构造方法是私有的,不允许在其他地方随便创建。
单例的优点非常明确:因为只有一个实例,所以减少了内存的开销,尤其是创建和销毁的时候减少了资源浪费。避免了对资源的多重占用。
单例的缺点呢:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
单例的使用场景
- 连接池:一个系统中一个数据源的连接池往往都是一个实例。
- 线程池:同上。
- 计数器:不用每次刷新都在数据库里加一次,用单例先缓存起来
- 创建一次消耗的资源过多的类对象。
单例的实现
单例有7种实现方式
方式1:线程不安全的懒汉模式
直接上菜:
- 构造方法私有化
- 提供静态的私有的本类实例对象作为成员变量
- 提供公共的静态的获取本类对象的方法
/** * @author 戴着假发的程序员 */
public class Singleton {
// 构造方法私有化
private Singleton(){
}
// 私有静态当前类对象作为成员变量
private static Singleton instance;
// 静态的公共的可以获取当前类实例对象的方法
public static Singleton getInstance(){
// 判断instance是否存在,如果存在就直接返回,如果不存在就创建
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
上面的程序非常简单:如果要获取Singleton类的实例对象可以这样写Singleton.getInstance()
你会发现:
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);// true 额。。。。。多执行几次,有可能也会是false
说明:
- 这个方式一开始并没有创建instance,在第一次使用的时候才会创建,这才是懒汉模式的真谛。有效的实现了延迟加载。节省了内存。
- 这个方式不是线程安全的,可能会有隐患。
- 如果线程A调用getInstance方法,判断了instance为null,正准备创建对象,结果CPU不给资源了,于是就稍微停了一会,这是线程B也调用了getInstance方法,结果判断instance依然是null,于是就创建了instance对象。之后线程A开始执行,线程A不会再判断了,直接创建instance对象,这样的话这个类的对象就被创建了两次。
方式2:线程安全的懒汉模式
上菜:
- 构造方法私有化
- 提供静态的私有的本类实例对象作为成员变量
- 提供公共的静态的同步的获取本类对象的方法
/** * @author 戴着假发的程序员 */
public class Singleton1 {
// 构造方法私有化
private Singleton1(){
}
// 私有静态当前类对象作为成员变量
private static Singleton1 instance;
// 静态的公共的可以获取当前类实例对象的方法
public static synchronized Singleton1 getInstance(){
// 判断instance是否存在,如果存在就直接返回,如果不存在就创建
if(instance == null){
instance = new Singleton1();
}
return instance;
}
}
这个测试吗很简单:
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);// true 额。。。这里无论执行多少次都是true
说明:
- 这种方式很明显就是在getInstance的方法上增加synchronized修饰符,这样就成功避免了线程安全的问题。
- 但是我们都知道一旦加锁了,程序效率就会略有降低(不过呢,在JDK1.6之后synchronized做了优化,如果没有大量并发的情况下其实效率影响也不大)
方式3:饿汉模式
饿汉模式关键在这个“饿”字。额… 记得以前我吃饭吃的很着急的时候,我亲爱的老母亲就说:“慢点吃,搞得饿死鬼上身似得”。非常庆幸的是老母亲现在也会这样说我。哈哈哈哈哈。。。。
言归正传:饿汉模式就是在一开始的时候直接创建实例对象
- 构造方法私有化
- 提供静态的私有的本类实例对象作为成员变量,并且直接实例化
- 提供公共的静态的获取本类对象的方法
/** * @author 戴着假发的程序员 */
public class Singleton2 {
// 构造方法私有化
private Singleton2(){
}
// 私有静态当前类对象作为成员变量
private static Singleton2 instance = new Singleton2();
// 静态的公共的可以获取当前类实例对象的方法
public static synchronized Singleton2 getInstance(){
// 判断instance是否存在,如果存在就直接返回,如果不存在就创建
if(instance == null){
instance = new Singleton2();
}
return instance;
}
}
饿汉模式真的有点饿,一开始就创建了。
但是如果,现在不饿呢,直接把饭煮了是不是会有点浪费。
所以饿汉模式的问题就是如果一开始就把类对象创建好了, 但是这个对象长时间用不到,那么就是有些浪费资源了。
所以呀,还是要看实际的使用场景的。
方式4:双检锁/双重校验锁
程序,必须追求效率和省资源。所以我们希望单利模式中创建对象的方法也是效率高高滴。
但是我们一旦给方法加锁必然降低方法的执行效率,所以双重校验锁可能是一个不错的选择,既能能相对的提高程序效率,又能保证线程安全。
具体的实现就是:
/** * @author 戴着假发的程序员 */
public class Singleton3 {
// 构造方法私有化
private Singleton3(){
}
// 私有静态当前类对象作为成员变量
private static Singleton3 instance;
// 静态的公共的可以获取当前类实例对象的方法
public static Singleton3 getInstance(){
// 判断instance是否存在,如果存在就直接返回,如果不存在就创建
if (instance==null) {
// 上锁
synchronized (Singleton3.class) {
// 再次判断instance是否存在,如果存在就直接返回,如果不存在就创建
if (instance == null) {
instance = new Singleton3();
}
}
}
return instance;
}
}
方式5:静态内部类
静态内部类的实现方式也叫登记式。是个啥情况呢:
直接上菜:
/** * @author 戴着假发的程序员 */
public class Singleton4 {
// 构造方法私有化
private Singleton4(){
}
// 准备一个静态内部类
private static class SingletonHolder{
// 申明并且实例化一个外部单利类的实例对象,并且设置为常量。
private final static Singleton4 INSTANCE = new Singleton4();
}
// 静态的公共的可以获取当前类实例对象的方法
public static Singleton4 getInstance(){
// 直接返回内部类的成员常量
return SingletonHolder.INSTANCE;
}
}
这种方式有啥用?
- 在没有调用getInstance方法之前,静态内部了会延迟加载也就是对象的创建会延迟。
- 利用classloader的机制确保了在创建实例是肯定是单线程的。
- 当然了如果你不希望实例对象延迟加载那还是使用饿汉模式比较好。
方式6:枚举
这个方式实在太简单,也好理解(当然你首先要知道啥是枚举)。所以就不多做解释了。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
关于单利模式的情况就是这样了。
还有其他的并发编程相关的内容,我会持续更新,欢迎关注。
我是”起点编程“的"戴着假发的程序员" 欢迎关注…欢迎评论。。。。。
起点编程-是你我的未来…
边栏推荐
- Processes and threads
- 金额计算用 BigDecimal 就万无一失了?看看这五个坑吧~~
- Illustrated network: what is virtual router redundancy protocol VRRP?
- Numpy np.max和np.maximum实现relu函数
- C语言 AES加解密
- ASP.NET-酒店管理系统
- Touch and screen automatic rotation debugging
- Based on MCU, how to realize OTA differential upgrade with zero code and no development?
- How to: configure ClickOnce trust prompt behavior
- 如何成为一名高级数字 IC 设计工程师(1-4)Verilog 编码语法篇:表达式
猜你喜欢

00后抛弃互联网: 毕业不想进大厂,要去搞最潮Web3

软件测试周刊(第78期):你对未来越有信心,你对现在越有耐心。

Understand go language context in one article

FL Studio 20 unlimited trial fruit arranger Download

Résumé des questions d'entrevue (2) Modèle io, ensemble, principe NiO, pénétration du cache, avalanche de rupture

Excel表格转到Word中,表格不超边缘纸张范围

面試題總結(2) IO模型,集合,NIO 原理,緩存穿透,擊穿雪崩

【obs】obs的ini格式的ConfigFile

Excel快速跨表复制粘贴

Summary of interview questions (2) IO model, set, NiO principle, cache penetration, breakdown avalanche
随机推荐
One hot code
Program process management tool -go Supervisor
Internet socket (non) blocking write/read n bytes
AMS series - application startup process
Leetcode 46: full arrangement
AI模型看看视频,就学会了玩《我的世界》:砍树、造箱子、制作石镐样样不差...
Tablespace creation management and control file management
金额计算用 BigDecimal 就万无一失了?看看这五个坑吧~~
ORACLE进阶(一) 通过EXPDP IMPDP命令实现导dmp
Mmc5603nj geomagnetic sensor (Compass example)
【学习笔记】dp 状态与转移
How to get started embedded future development direction of embedded
C language utf8toutf16 (UTF-8 characters are converted to hexadecimal encoding)
Key switch: press FN when pressing F1-F12
.\vmware-vdiskmanager.exe -k “c:\\xxxxx.vmdk”
动态规划(区间dp)
PHP server interacts with redis with a large number of close_ Wait analysis
repo ~ 常用命令
phpcms 提示信息頁面跳轉showmessage
AMS Series 1 - AMS startup process