单例模式 (饿汉、懒汉)

public class ThreadDemo22 {

/*

  • 饿汉模式 单例实现

  • “饿” —— 只要类被加载,实例就会立刻被创建 (实例创建的时机比较早)

  • */

static class Singleton{

// 把构造方法变成私有的,此时在该类的外部就无法 new 这个类的实例了

private Singleton(){

}

// 再来创建一个 static 的成员,表示 Singleton 类唯一的实例

// static 成员 和类相关,和实例是无关的

// 类在内存中只有一份,static 成员也只有一份

private static Singleton instance = new Singleton();

public static Singleton getInstance(){

return instance;

}

}

public static void main(String[] args) {

// 此处 new 可以,是因为 Singleton 是 ThreadDemo22 的内部类,

// ThreadDemo 是可以访问 内部类的 private 成员的

Singleton s = new Singleton();

// 此处的 getInstance 就是获取该类实例的唯一方式,不应该使用其他方式来创建实例

Singleton s1 = Singleton.getInstance();

Singleton s2 = Singleton.getInstance();

System.out.println(s1 == s2);

}

}

输出结果:

在这里插入图片描述

懒汉模式

当类被加载的时候,不会立刻实例化

等到第一次使用这个实例的时候,再实例化

public class ThreadDemo23 {

/*

  • 懒汉模式

  • */

static class Singleton{

private Singleton() {

}

// 类加载的时候,没有立刻实例化

// 第一次调用 getInstance 时,才真正的实例化

private static Singleton instance = null;

public static Singleton getInstance(){

if(instance == null){

instance = new Singleton();

}

return instance;

}

}

}

类加载的时候,没有立刻实例化;第一次调用 getInstance 时,才真正的实例化

若代码,一直没有调用 getInstance,此时实例化的过程也就被省略掉了 —— 延时加载

一般认为,“懒汉模式” 比 “饿汉模式” 的效率更高~

原因: 懒汉模式有很大的可能是 “实例是用不到”,此时就节省了实例化的开销

线程安全问题分析:

思考: “饿汉模式” 和 “懒汉模式”,哪个是线程安全的???

(线程安全:假设多个线程并发的调用 getInstance 方法,是否会导致逻辑错误)

啥样的情况会导致线程不安全???

线程安全问题,我们提到有以下原因:

  • 线程是抢占式执行的

  • 修改操作不是原子的

  • 多个线程尝试修改同一个变量(单例模式常出现)

  • 内存可见性

  • 指令重排序

饿汉模式—线程安全;

懒汉模式—线程不安全

分析:

1.饿汉模式:

实例化时机是在类加载的时候,而类加载只有一次机会,不可能并发执行

当多线程并发的调用 getInstance 时,由于 getInstance 里只做了一件事:读取 instance 实例的地址,相当于多个线程同时读取同一个变量;因此,饿汉模式是线程安全的

2.懒汉模式:

多线程同时调用 getInstance 时,getInstance 中做了四件事:①读取 instance 的内容;②判断是否为null;③若 instance 为 null,就 new 实例;④返回实例的地址

当 new 实例的时候,就会修改 instance 的值

画图分析:

在这里插入图片描述

懒汉模式,后续调用 getInstance 都不会触发线程安全问题,只有在第一次实例化的时候,多线程并发调用 getInstance 时,会有线程不安全问题的风险

如何解决线程安全问题??

那么,如何改进 懒汉模式,让代码变成线程安全的???

方法1— 加锁 synchronized

  • 改法1

这样写,读取判断操作,和new 修改操作 仍然不是原子的,故这样修改不可行!!

private static Singleton instance = null;

public static Singleton getInstance(){

if(instance == null){

synchronized (Singleton.class){

instance = new Singleton();

}

}

return instance;

}

  • 改法2

这么加是可以保证原子性的

private static Singleton instance = null;

public static Singleton getInstance(){

synchronized (Singleton.class){

if(instance == null){

instance = new Singleton();

}

}

return instance;

}

上述改法,虽然解决了线程不安全的问题,但仍然会问题 — 效率问题,

画图分析:

在这里插入图片描述

  • 改法3

private static Singleton instance = null;

synchronized public static Singleton getInstance(){

if(instance == null){

instance = new Singleton();

}

return instance;

}

画图和改法2 差不多,只不过 return 操作是在释放锁内部来完成的

由于 return 只是在读,所以这个操作放到锁里边或者锁外边不影响结果

虽然改法2 和 改法3 都可行,但是改法2 的锁粒度更小,改法3 的锁粒度更大

锁的粒度: 锁中包含的代码越多,就认为锁粒度越大

一般,我们希望锁的粒度小一点更好,因为锁的粒度越大,说明这段代码的并发能力就越受限

方法2 — 双重 if

由于加锁是为了避免第一次创建实例时线程不安全,后面在进行加锁解锁操作都只会降低性能,所以外层再添加 if 判断,当发现其为空时才加锁,否则直接返回已经创建好的实例对象,减少了加锁解锁的次数,从而提高性能

private static Singleton instance = null;

public static Singleton getInstance(){

if(instance == null){

synchronized (Singleton.class){

if(instance == null){

instance = new Singleton();

}

}

}

return instance;

}

画图分析:

  • 实例化之前:

此处有多个读操作,可能会被编译器优化:只有第一次读,才从内存中读,后续的读就是从CPU中读取寄存器(上次读到的结果)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

更多:Java进阶核心知识集

包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等

image

高效学习视频

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
g.cn/images/e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

更多:Java进阶核心知识集

包含:JVM,JAVA集合,网络,JAVA多线程并发,JAVA基础,Spring原理,微服务,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存等等

[外链图片转存中…(img-TE6vskfv-1713554972623)]

高效学习视频

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值