当前位置:网站首页>单例模式 (Singleton)
单例模式 (Singleton)
2022-07-30 17:59:00 【Java咩】
目录
单例模式 (Singleton)
在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。
一、应用场景
- 只需要一个实例
- 比如各种Mgr
- 比如各种Factory
二、分类与实现
1. 饿汉模式
类加载到内存后,就实例化一个单例,JVM保证线程安全
优点:
简单实用,推荐使用!
唯一缺点:
不管用到与否,类装载时就完成实例化 。Class.forName(“”) (话说你不用的,你装载它干啥)
代码实现:
实现方式一:
public class Mgr01 {
// 类加载时对象被实例化
private static final Mgr01 INSTANCE = new Mgr01();
// 构造方法私有,保证不会被多次实例化
private Mgr01() {
};
// 返回实例化对象
public static Mgr01 getInstance() {
return INSTANCE;
}
public void m() {
System.out.println("m");
}
// for test
public static void main(String[] args) {
Mgr01 m1 = Mgr01.getInstance();
Mgr01 m2 = Mgr01.getInstance();
System.out.println(m1 == m2); // true 证明对象只会被实例化一次
}
}
实现方式二:
public class Mgr02 {
private static final Mgr02 INSTANCE;
// 通过静态代码块让对象实例化
static {
INSTANCE = new Mgr02();
}
private Mgr02() {
};
public static Mgr02 getInstance() {
return INSTANCE;
}
public void m() {
System.out.println("m");
}
public static void main(String[] args) {
Mgr02 m1 = Mgr02.getInstance();
Mgr02 m2 = Mgr02.getInstance();
System.out.println(m1 == m2); // true 证明对象只会被实例化一次
}
}
2. 懒汉模式 (lazy loading)
为了解决 饿汉模式
中,不管用到与否,类装载时就完成实例化的问题,提出了懒汉模式。
即,只有类在被用到时,才会被实例化。
优点:
达到了按需初始化的目的
缺点:
可能会带来线程不安全的问题
实现方式一:(线程不安全)
类在被用到时,才会被实例化,即,如果该对象没有被实例化,才会去实例化该对象性。
但是会造成线程不安全的新问题。
public class Mgr03 {
private static Mgr03 INSTANCE;
private Mgr03() {
}
public static Mgr03 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr03();
}
return INSTANCE;
}
public void m() {
System.out.println("m");
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->
System.out.println(Mgr03.getInstance().hashCode())
).start();
}
}
}
输出结果:
769879054
391275521
819827238
819827238
391275521
956261507
实现方式二:(线程安全,但是效率低下)
为了解决线程不安全的问题,我们想到了可以通过 synchronized
解决。
但是,同样也带来了效率低下的新问题。
public class Mgr04 {
private static Mgr04 INSTANCE;
private Mgr04() {
}
// 加 synchronized 保证线程安全
public static synchronized Mgr04 getInstance() {
// 如果该类位被实例化,则进行实例化
if (INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr04();
}
return INSTANCE;
}
public void m() {
System.out.println("m");
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->{
System.out.println(Mgr04.getInstance().hashCode());
}).start();
}
}
}
实现方式三:(效率提升,线程不安全)
为了解决增加 synchronized
后效率低下的问题,可以尝试减小 synchronized
的作用范围。
但是发现不可行,有带来了线程不安全的问题。
public class Mgr05 {
private static Mgr05 INSTANCE;
private Mgr05() {
}
public static Mgr05 getInstance() {
if (INSTANCE == null) {
//妄图通过减小同步代码块的方式提高效率,然后不可行
synchronized (Mgr05.class) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr05();
}
}
return INSTANCE;
}
public void m() {
System.out.println("m");
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->{
System.out.println(Mgr05.getInstance().hashCode());
}).start();
}
}
}
运行结果:
769879054
769879054
1030610325
769879054
1030610325
956261507
实现方式四:(效率提升,线程安全)
为了解决上述问题,可与采用双重判断来实现懒汉模式
public class Mgr06 {
private static volatile Mgr06 INSTANCE; //JIT
private Mgr06() {
}
public static Mgr06 getInstance() {
if (INSTANCE == null) {
synchronized (Mgr06.class) {
//双重检查
if(INSTANCE == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new Mgr06();
}
}
}
return INSTANCE;
}
public void m() {
System.out.println("m");
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->{
System.out.println(Mgr06.getInstance().hashCode());
}).start();
}
}
}
实现方式五:(通过静态内部类方式来实现懒汉模式)
JVM保证单例,加载外部类时不会加载内部类,这样可以实现懒加载
public class Mgr07 {
// 将默认构造方法私有化
private Mgr07() {
}
// 声明一个静态内部类 持有单例类 Mgr7
private static class Mgr07Holder {
private final static Mgr07 INSTANCE = new Mgr07();
}
public static Mgr07 getInstance() {
return Mgr07Holder.INSTANCE;
}
public void m() {
System.out.println("m");
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->{
System.out.println(Mgr07.getInstance().hashCode());
}).start();
}
}
}
3. 通过枚举类来实现单例模式
不仅可以解决线程同步,还可以防止反序列化。
public enum Mgr08 {
INSTANCE;
public static Mgr08 getInstance() {
return INSTANCE;
}
public void m() {
System.out.println("m");
}
public static void main(String[] args) {
for(int i=0; i<100; i++) {
new Thread(()->{
System.out.println(Mgr08.getInstance().hashCode());
}).start();
}
}
}
三、写在后面
欢迎关注,会经常记录一些设计模式学习的笔记。
欢迎随时留言讨论,与君共勉,知无不答!
边栏推荐
猜你喜欢
【HarmonyOS】【FAQ】鸿蒙问题合集4
MySQL中的存储过程(详细篇)
Vulkan与OpenGL对比——Vulkan的全新渲染架构
想要写出好的测试用例,先要学会测试设计
Basic knowledge points in js - BOM
Confluence OGNL注入漏洞复现(CVE-2022-26134)
Moralis去中心化Web3应用开发教程
【HMS core】【Analytics Kit】【FAQ】如何解决华为分析付费分析中付款金额显示为0的问题?
ESP8266-Arduino programming example-HC-SR04 ultrasonic sensor driver
One year after graduation, I was engaged in software testing and won 11.5k. I didn't lose face to the post-98 generation...
随机推荐
ARC在编译期和运行期做了什么
5分钟搞懂MySQL - 行转列
LayaBox---TypeScript---类型推论
测试行业干了5年,从只会点点点到了现在的测试开发,总算是证明了自己
第十六期八股文巴拉巴拉说(MQ篇)
图解LeetCode——11. 盛最多水的容器(难度:中等)
《自然语言处理实战入门》---- 文本样本扩展小技巧:使用回译技术进行样本增强
使用postman调接口报Content type ‘text/plain;charset=UTF-8‘ not supported
SQL存储过程详解
高级语言垃圾回收思路和如何减少性能影响原理分析
Mongo for infrastructure
Informatics Olympiad 1915: [01NOIP Popularization Group] Greatest Common Divisor and Least Common Multiple | Luogu P1029 [NOIP2001 Popularization Group] The problem of the greatest common divisor and
LayaBox---TypeScript---枚举
What are the applications of X-rays?
一个 15 年 SAP ABAP 开发人员分享的 SAPGUI 一些个性化设置和实用小技巧
数据库系统原理与应用教程(069)—— MySQL 练习题:操作题 95-100(十三):分组查询与聚合函数的使用
[OC study notes] attribute keyword
weiit新零售小程序如何探索数字化门店的破局之路
Informatics Olympiad All-in-One 1966: [14NOIP Popularization Group] Scale Simplification | Luogu P2118 [NOIP2014 Popularization Group] Scale Simplification
Linux-安装MySQL(详细教程)