我准备了一份10万字的java全面知识总结
领取方式
https://pan.quark.cn/s/4e6e3d03407a
在并发编程中,绕不过去的几个关键词就是 CAS、引用类型、ThreadLocal。这些概念在日常开发里经常遇到,不搞明白容易踩坑。整理一下关键点,尽量用清晰简单的方式说清楚,希望对实际开发有帮助。
一、CAS 是什么
CAS,全称 Compare And Swap,翻译成“比较并交换”。简单说,就是一种原子操作,用来实现并发情况下的线程安全更新。
不加锁,但能保证某个变量在并发更新时是安全的。JDK 的很多类,比如 AtomicInteger
、ConcurrentHashMap
,底层就是基于 CAS 实现的。
二、CAS 的原理
CAS 依赖 CPU 提供的原子指令。工作过程有三个核心变量:
- 当前内存值(V)
- 期望值(E)
- 要更新的新值(N)
操作过程:先比较 V 和 E 是否相等。如果相等,说明没人改动过这个值,可以把 V 更新为 N。如果不等,说明有别的线程改过,不执行更新。
通俗点说,相当于“这值还是我刚看到的那个吗?如果是我就改,如果不是就算了”。这是非阻塞的一种实现方式,线程不会被锁住,只是失败后可以选择重试。
三、CAS 的缺点
CAS 是有问题的,不是万能药。几个主要缺点必须注意:
-
ABA 问题
假设线程 A 读取到值是 10,然后线程 B 把它改成 20,又改回 10。线程 A 再做 CAS,发现值没变,但其实已经被别人动过。这就可能出问题。
解决办法:JDK 提供了AtomicStampedReference
这类带版本号的工具类。 -
自旋导致资源浪费
如果一直 CAS 失败,会不断重试,这叫自旋。高并发下,反复重试浪费 CPU。 -
只能操作一个变量
CAS 是单变量操作。多个变量同时更新需要加锁或用更复杂的方案,比如AtomicReference
组合结构。
四、引用类型有哪些?区别在哪?
Java 里的引用分四种:强引用、软引用、弱引用、虚引用。和 GC 密切相关,搞清楚这些,对内存优化很关键。
-
强引用(Strong Reference)
最常见的引用方式。只要对象被强引用指着,就不会被回收。 -
软引用(Soft Reference)
用于内存敏感缓存。如果内存充足,软引用对象不会回收;内存紧张时,会被 GC 回收。适合缓存场景,比如图片加载、对象池。 -
弱引用(Weak Reference)
一旦发生 GC,不管内存是否紧张,都会被回收。适合使用后立刻就可丢弃的对象。 -
虚引用(Phantom Reference)
本身不会影响对象生命周期,只用来监控对象是否被回收。配合ReferenceQueue
使用,可用于资源释放、清理。
核心区别:回收时机不同。越往后,引用越“弱”,GC 越容易回收。
五、ThreadLocal 的原理
ThreadLocal 用于为每个线程提供独立的变量副本。常用于存储线程相关的数据,比如用户信息、事务控制、数据库连接等。
底层原理:
- 每个线程内部维护一个 ThreadLocalMap,key 是 ThreadLocal 实例,value 是实际存储的数据。
- 同一个 ThreadLocal 实例在不同线程中有不同的副本,互不影响。
- 数据只在线程内部访问,线程结束后,数据也会随之消失。
这也解释了为什么用 ThreadLocal 可能会内存泄漏。因为 ThreadLocalMap 的 key 是弱引用,但 value 是强引用。如果 key 被 GC 回收但 value 没清理,Map 就可能一直持有这个 value。
解决办法:
- 手动调用
remove()
清理。 - 尽量用完即清,控制生命周期。
- 在线程池中使用 ThreadLocal 要特别注意,因为线程池线程不会主动销毁,ThreadLocal 容易残留。
总结一下:
- CAS 适合实现非阻塞并发控制,但不能滥用,要清楚它失败的场景。
- 引用类型关系到对象回收机制,清楚四种引用能帮助写出更稳健的内存管理逻辑。
- ThreadLocal 虽然使用方便,但也要注意它的副作用,尤其是线程池场景下的内存泄漏风险。
理解这些原理不是为了秀技术,是为了在实际项目里少出错、出错时能排查得准。问题都是细节带来的,理解底层比靠经验更保险。