冰河技术
导读
♻学习路线
  • 面试必问系列

    • 面试必问
  • 架构与模式

    • Java极简设计模式
    • 实战高并发设计模式
  • Java核心技术

    • Java8新特性
    • IOC核心技术
    • JVM调优技术
  • 容器化核心技术

    • Dockek核心技术
  • 分布式存储

    • Mycat核心技术
  • 数据库核心技术

    • MySQL基础篇
  • 服务器核心技术

    • Nginx核心技术
  • 渗透核心技术

    • 渗透实战技术
  • 底层技术
  • 源码分析
  • 基础案例
  • 实战案例
  • 面试
  • 系统架构
  • Spring6核心技术
  • 分布式事务

    • 分布式事务系列视频
  • SpringBoot
  • SpringCloudAlibaba
  • 🔥AI大模型项目

    • 一站式AI智能平台
    • AI智能客服系统
    • AI智能问答系统
    • 实战AI大模型
  • 中间件项目

    • 手写高性能Redis组件
    • 手写高性能脱敏组件
    • 手写线程池项目
    • 手写高性能SQL引擎
    • 手写高性能Polaris网关
    • 手写高性能RPC项目
  • 高并发项目

    • 分布式IM即时通讯系统(新)
    • 分布式Seckill秒杀系统
    • 实战高并发设计模式
  • 微服务项目

    • 简易电商脚手架项目
  • 手撕源码

    • 手撕Spring6源码
🌍知识星球
  • 总览

    • 《书籍汇总》
  • 出版图书

    • 《深入理解高并发编程:核心原理与案例实战》
    • 《深入理解高并发编程:JDK核心技术》
    • 《深入高平行開發:深度原理&專案實戰》
    • 《深入理解分布式事务:原理与实战》
    • 《MySQL技术大全:开发、优化与运维实战》
    • 《海量数据处理与大数据技术实战》
  • 电子书籍

    • 《实战高并发设计模式》
    • 《深入理解高并发编程(第2版)》
    • 《深入理解高并发编程(第1版)》
    • 《从零开始手写RPC框架(基础篇)》
    • 《SpringCloud Alibaba实战》
    • 《冰河的渗透实战笔记》
    • 《MySQL核心知识手册》
    • 《Spring IOC核心技术》
  • 关于自己
  • 关于学习
  • 关于职场
B站
Github
导读
♻学习路线
  • 面试必问系列

    • 面试必问
  • 架构与模式

    • Java极简设计模式
    • 实战高并发设计模式
  • Java核心技术

    • Java8新特性
    • IOC核心技术
    • JVM调优技术
  • 容器化核心技术

    • Dockek核心技术
  • 分布式存储

    • Mycat核心技术
  • 数据库核心技术

    • MySQL基础篇
  • 服务器核心技术

    • Nginx核心技术
  • 渗透核心技术

    • 渗透实战技术
  • 底层技术
  • 源码分析
  • 基础案例
  • 实战案例
  • 面试
  • 系统架构
  • Spring6核心技术
  • 分布式事务

    • 分布式事务系列视频
  • SpringBoot
  • SpringCloudAlibaba
  • 🔥AI大模型项目

    • 一站式AI智能平台
    • AI智能客服系统
    • AI智能问答系统
    • 实战AI大模型
  • 中间件项目

    • 手写高性能Redis组件
    • 手写高性能脱敏组件
    • 手写线程池项目
    • 手写高性能SQL引擎
    • 手写高性能Polaris网关
    • 手写高性能RPC项目
  • 高并发项目

    • 分布式IM即时通讯系统(新)
    • 分布式Seckill秒杀系统
    • 实战高并发设计模式
  • 微服务项目

    • 简易电商脚手架项目
  • 手撕源码

    • 手撕Spring6源码
🌍知识星球
  • 总览

    • 《书籍汇总》
  • 出版图书

    • 《深入理解高并发编程:核心原理与案例实战》
    • 《深入理解高并发编程:JDK核心技术》
    • 《深入高平行開發:深度原理&專案實戰》
    • 《深入理解分布式事务:原理与实战》
    • 《MySQL技术大全:开发、优化与运维实战》
    • 《海量数据处理与大数据技术实战》
  • 电子书籍

    • 《实战高并发设计模式》
    • 《深入理解高并发编程(第2版)》
    • 《深入理解高并发编程(第1版)》
    • 《从零开始手写RPC框架(基础篇)》
    • 《SpringCloud Alibaba实战》
    • 《冰河的渗透实战笔记》
    • 《MySQL核心知识手册》
    • 《Spring IOC核心技术》
  • 关于自己
  • 关于学习
  • 关于职场
B站
Github
  • 专栏开篇

    • 开篇:用讲故事的形式带你彻底吃透并发设计模式
  • 第一篇:不可变模式

    • 第01章:这特么到底是哪里不对
    • 第02章:原来问题出在这里
    • 第03章:有哪些方法能够解决并发问题
    • 第04章:可变类的线程安全问题
    • 第05章:实现不可变类解决线程安全问题
    • 第06章:实现消息聚合发送系统
    • 第07章:JDK中的等效不可变类
  • 第二篇:保护性暂挂模式

    • 第08章:线程的流转状态
    • 第09章:解决交易过程加锁的安全性问题
    • 第10章:解决交易过程性能与死锁问题
    • 第11章:使用保护性暂挂模式优化交易系统性能
    • 第12章:基于护性暂挂模式实现监控报警系统
    • 第13章:保护性暂挂模式在JDK中的应用
  • 第三篇:两阶段终止模式

    • 第14章:线程还没执行完任务怎么就退出了
    • 第15章:到底什么是两阶段终止模式
    • 第16章:实现监控报警系统线程优雅退出
    • 第17章:两阶段终止模式在线程池中的应用
  • 第四篇:承诺模式

    • 第18章:这代码性能怎么这么差
    • 第19章:到底什么是承诺模式
    • 第20章:基于承诺模式优化社区电商项目
    • 第21章:文件同步助手项目性能太差原因分析
    • 第22章:基于承诺模式优化文件同步助手项目
    • 第23章:承诺模式在FutureTask类中的应用
  • 第五篇:生产者消费者模式

    • 第24章:面向C端的个人文库系统崩了
    • 第25章:个人文库系统资源耗尽问题分析
    • 第26章:优化面向C端的个人文库系统
    • 第27章:消息堆积问题场景分析
    • 第28章:消息堆积问题解决方案
    • 第29章:生产者消费者模式在线程池中的应用
  • 第六篇:主动对象模式

    • 第30章:重大事故访问商品链接404
    • 第31章:访问商品链接404原因分析
    • 第32章:到底什么是主动对象模式
    • 第33章:基于主动对象模式优化社区电商系统
    • 第34章:主动对象模式在线程池中的应用
  • 第七篇:线程池模式

    • 第35章:服务器内存爆了
    • 第36章:无法创建新的本地线程
    • 第37章:优化社区电商系统优惠券服务
    • 第38章:线程池核心参数解析
    • 第39章:线程池执行任务源码深度解析
    • 第40章:实现手写线程池
  • 第八篇:线程特有存储模式

    • 第41章:用户信息怎么错乱了
    • 第42章:到底什么是线程特有存储
    • 第43章:解决格式化时间的线程安全问题
    • 第44章:线程特有存储模式在JDK中的应用
    • 第45章:ThreadLocal内存泄露分析
  • 第九篇:串行线程封闭模式

    • 第46章:导出报表数据错乱了
    • 第47章:到底什么是串行线程封闭模式
    • 第48章:优化报表系统导出数据功能
  • 第十篇:主仆模式

    • 第49章:统计个数据性能太差了
    • 第50章:到底什么是主仆模式
    • 第51章:基于主仆模式优化统计热点商品功能
    • 第52章:主仆模式在JDK中的应用
  • 第十一篇:流水线模式

    • 第53章:统计个交易额也能这么慢
    • 第54章:到底什么是流水线模式
    • 第55章:基于流水线模式优化实时统计交易额功能
    • 第56章:流水线模式在Netty中的应用
  • 第十二篇:半同步半异步模式

    • 第57章:支付系统性能太差了
    • 第58章:到底什么是半同步半异步模式
    • 第59章:使用半同步半异步模式优化支付系统
    • 第60章:如何处理消息堆积问题
  • 专栏总结

    • 结尾:并发设计模式整体专栏总结

《并发设计模式》第05章-不可变模式-实现不可变类解决线程安全问题

作者:冰河
星球:http://m6z.cn/6aeFbs
博客:https://binghe.site
文章汇总:https://binghe.site/md/all/all.html
源码获取地址:https://t.zsxq.com/0dhvFs5oR

沉淀,成长,突破,帮助他人,成就自我。

  • 本章难度:★★☆☆☆
  • 本章重点:掌握不可变模式的核心原理,熟练掌握不可变类的实现方式与原则,能够将不可变类熟练应用到自身实际项目中解决线程安全问题。

大家好,我是冰河~~

“本以为自己写的代码没问题,被老大一看竟然有这么多问题,不过还好算是明白哪里出问题了,也知道了出问题的原因,哎,自己重新实现吧,就有点找不到思路了。不可变,不可变,怎么设计这个类才是不会变化的呢?我感觉照老大之前的分析来看,很多实体类都会提供无参和有参构造方法,也会提供修改成员变量的getter/setter方法,这不就都是可变类了吗?”,小菜一直在心里嘀咕着。

一、情景再现

小菜听老王讲解的解决并发问题的方案后,对不可变类很感兴趣,于是下班后,自己尝试以“不可变类”模拟写了一个检票的功能小demo。满怀信心的想让老王给他看看,结果万万没想到。

二、寻求帮助

听完老王分析小菜的代码为何不是不可变类后,小菜下班回去认真思考了很久,可还是不知道怎么写一个不可变类。“还是到公司上班的时候问问老大吧。”,小菜最终还是要寻求老王的帮助了。

到了第二天,小菜还是起的特别早,早早来到公司,没想到老王已经坐在工位上忙事情了。“老大,今天来的真早啊”,小菜说道。

老王抬起头一看是小菜来了,便随口说道:“早,小菜,昨天跟你分析的代码听懂了吧?怎么样,自己写了不可变类解决线程安全问题吗?”。

“没有,我昨天下班到家想了很久,都不知道怎么写。很多实体类不是都会提供无参和有参构造方法吗?也会提供修改成员变量的getter/setter方法,这样的话,很多类都是可变的了,我确实没想出来改怎么写一个不可变类”。小菜回应道。

要不怎么说老王是个大好人呢?老王听后便说道:“我们拿着电脑去会议室吧,我给你讲讲怎么写不可变类”。

“好呀”,小菜听后,很高兴的回应到。

就这样,老王和小菜两个人都拿着电脑走进去了会议室。。。

PS:老王真特么是个大好人。。。

三、不可变类的原则

二人走到会议室后,老王放下电脑后说到:“我先给你讲讲写不可变类必须要满足的条件,也就是不可变类必须要遵循的原则”。

“好的”,小菜回应道。

“说起不可变类,必须要遵循。。。”,老王巴拉巴拉的说起来。

总体上讲,老王总结了如下几点:

(1)必须使用final关键字修饰不可变类,禁止被其他类继承,防止子类改变其方法和行为。

(2)必须使用final关键字修饰类中的所有成员变量,避免成员变量被修改。使用final修饰成员变量,也能保证成员变量在多线程并发环境下被其他线程访问时,一定是初始化完成的。

(3)必须使用private关键字修饰所有成员变量,防止子类和其他类通过成员变量的引用改变变量值。

(4)类中禁止提供修改内部状态的公开方法。

(5)创建对象的过程中,this关键字不能泄露给其他类,防止其他类(比如这个类的匿名内部类)在对象创建过程中修改状态。

(6)类中的任何字段,如果引用了状态可变的对象,比如集合、数组等,则这些字段也必须使用private修饰,这些字段以及字段的引用不能对外暴露,返回这些字段值时,必须进行防御性复制。

“这些原则能听懂吗?”,老王问小菜。

“有些能,有些就不能了”,小菜回答道。

“没事,我带着你把你上次的代码修改下,你就明白了”。老王说着便打开了自己的开发环境。

“好的”,小菜回应道。

四、实现不可变类

“不可变类之前我们也说过,它就是一经创建就不会再被改变的类”,老王边说边开始敲代码。

“首先,按照不可变类的原则来讲,我们先给User类和它的成员变量name与idCard都使用final关键字修饰,此时在User类中就会报错,要去掉set()方法,添加一个构造方法”,说话间,老王就将修改后的User类敲出来了(老王真牛)。

User类的源码详见:concurrent-design-patterns-immutable工程下的io.binghe.concurrent.design.demo.right.User。

public final class User {
    private final String name;
    private final Long idCard;
    public User(String name, Long idCard) {
        this.name = name;
        this.idCard = idCard;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", idCard=" + idCard +
                '}';
    }
}

“这样User类就不能被其他类继承,也就是说,User类不会有任何子类来改变它的方法和行为,并且User里的成员变量都是使用final关键字修饰,也不会有其他方法来修改成员变量的值,User类能结合不可变类的原则看懂吗?”,老王继续说道。

“嗯,这里可以看懂”,小菜回应道。

“接下来,我们再来看TicketCheck类,之前你写的TicketCheck类的updateUser()方法中会分别传入userName和idCard来修改通过检票的人”,说着,老王打开了之前小菜写的代码,如下所示。

查看全文

加入冰河技术知识星球,解锁完整技术文章与完整代码

在 GitHub 上编辑此页
上次更新: 2026/4/29 16:18
Contributors: binghe001
Prev
第04章:可变类的线程安全问题
Next
第06章:实现消息聚合发送系统
阅读全文
×

扫码或搜索:冰河技术
发送:290992
即可立即永久解锁本站全部文章

星球会员
跳转链接