当前位置: 首页 > news >正文

【JUC】核心知识点归纳

JUC 核心知识点归纳

JUC(java.util.concurrent)是 Java 处理并发编程的核心包,涵盖线程创建线程安全、锁机制、并发集合等关键内容,是面试和开发中的重点。本文基于核心知识点整理,旨在帮助快速回顾、巩固核心内容。

一、线程基础:创建方式与状态流转

1. 线程的创建方式

Java 中创建线程的 4 种核心方式,适用于不同场景:

  • 继承 Thread 类:重写 run() 方法,直接调用 start() 启动线程(单继承限制,灵活性低)。
  • 实现 Runnable 接口:实现 run() 方法,通过 new Thread(Runnable) 启动(无继承限制,推荐)。
  • 实现 Callable 接口 + FutureTask:实现 call() 方法(支持返回值、可抛出异常),通过 FutureTask 封装后传入 Thread 启动,适用于需要任务结果的场景。
  • 通过线程池创建:使用 ExecutorsThreadPoolExecutor 创建线程池,从池中复用线程(推荐生产环境,降低线程创建 / 销毁开销)。

2. 线程的 6 种状态及流转

Java 中 Thread 类的 State 枚举定义了 6 种线程状态,流转逻辑清晰:

  1. 新建状态(NEW):创建线程对象(如 new Thread()),未调用 start()
  2. 就绪状态(Runnable):调用 start() 后,线程等待 CPU 执行权(抢到 CPU 后实际运行,但状态仍为 Runnable)。
  3. 阻塞状态(Blocked):运行中线程未获取到锁对象(如进入 synchronized 块但锁被占用)。
  4. 等待状态(Waiting):运行中线程调用 wait()join() 等方法,需被其他线程唤醒。
  5. 计时等待状态(Timed_Waiting):运行中线程调用 sleep(long)wait(long) 等带超时时间的方法。
  6. 结束状态(Terminated)run() 方法执行完毕或线程异常终止。

核心流转路径:NEW → Runnable → (Blocked/Waiting/Timed_Waiting)→ Runnable → Terminated

二、线程安全:核心定义与本质

1. 线程安全的定义

多线程并发访问共享资源(变量、对象、文件等)时,程序执行结果始终符合预期,无数据错乱、逻辑错误(如并发修改导致的数值异常)。

2. 线程安全的本质:3 大特性

线程安全问题的根源是共享资源的并发访问破坏了以下 3 个特性:

  • 原子性:操作不可分割(如 i++ 实际是 i = i + 1,分读取、计算、赋值 3 步,并发时易被打断)。
  • 可见性:线程对共享变量的修改,其他线程能立即感知(CPU 缓存导致修改未及时同步到主存)。
  • 有序性:代码执行顺序与编写顺序一致(编译器、CPU 为优化性能会重排序,并发时可能导致逻辑错误)。

三、线程池:核心优势与设计初衷

1. 为什么使用线程池?

线程的创建和销毁是高开销操作(需分配内存、切换内核态),频繁创建 / 销毁会消耗大量系统资源。线程池通过 “预先创建线程 + 复用线程” 解决该问题。

2. 线程池的核心好处

  • 资源复用:避免频繁创建 / 销毁线程,降低开销。
  • 资源管理:控制最大并发线程数,防止线程过多导致 CPU 过载、内存溢出。
  • 提高响应速度:线程已预先创建,任务提交后可立即执行,无需等待线程创建。
  • 便于监控与调优:支持统计任务执行情况、线程活跃度等。

四、ThreadLocal:线程隔离的核心工具

1. 什么是 ThreadLocal?

提供线程级别的数据存储机制,每个线程拥有独立的 ThreadLocal 变量副本,线程间互不干扰,实现线程隔离。

2. 核心作用与使用场景

(1)两大核心作用
  • 线程内数据传递:避免多层方法通过参数传递数据(如请求链路中的用户信息)。
  • 解决并发问题:为非线程安全的变量(如 SimpleDateFormat)创建线程独立实例,避免竞争。
(2)典型使用场景
  • 用户身份信息存储:拦截器鉴权后,将用户 ID、权限存入 ThreadLocal,后续链路直接获取。
  • 非线程安全变量隔离:如 SimpleDateFormat,通过 ThreadLocal 为每个线程创建独立实例。
  • 日志 / 链路追踪上下文:存储日志中的用户 ID、分布式追踪的 traceId,方便调试。
  • 数据库 Session 管理:MyBatis、Hibernate 用 ThreadLocal 存储会话,确保线程独立。

3. 实现原理

在这里插入图片描述

ThreadLocal 的线程隔离核心依赖 Thread 类中的 ThreadLocalMap 成员变量:

  1. ThreadLocalMap:本质是 ThreadLocal 内部的哈希表,维护一个 Entry 数组。
  2. Entry 对象:key 是 ThreadLocal 实例(弱引用),value 是存储的数据(强引用),通过 ThreadLocal 的 threadLocalHashCode 计算数组索引。
  3. 数据存取逻辑:
    • 存数据:Thread.currentThread().getThreadLocalMap().put(this, value)(this 即当前 ThreadLocal 实例)。
    • 取数据:Thread.currentThread().getThreadLocalMap().get(this)

4. 内存泄漏风险与解决方案

(1)为什么会内存泄漏?

在这里插入图片描述

  • 线程池中的核心线程会被复用,线程对 ThreadLocalMap 是强引用,导致 ThreadLocalMap 无法被 GC。

在这里插入图片描述

  • Entry 的 key(ThreadLocal)是弱引用,GC 时会被回收,但 value 是强引用,若未主动删除,会导致 value 无法回收,积累后内存泄漏。
(2)解决方案

在这里插入图片描述

使用完 ThreadLocal 后,必须在 finally 代码块中调用 remove() 方法,主动删除 value:

try {threadLocal.set(value);// 业务逻辑
} finally {threadLocal.remove(); // 关键:避免内存泄漏
}

五、锁机制:synchronized 与 ReentrantLock

1. synchronized:JVM 内置锁

(1)定义与原理
  • 语言层面的关键字,基于 JVM 内置的 Monitor 监视器锁 实现。
  • 字节码层面:通过 monitorenter(进入临界区)和 monitorexit(退出临界区)指令控制锁。
  • 锁状态存储:通过对象头的 Mark Word 记录锁状态(偏向锁、轻量级锁、重量级锁)。
(2)核心流程
  • 加锁:执行 monitorenter 时,检查 Monitor 的 Owner 字段,若为空则当前线程占用锁;否则线程进入 EntryList 阻塞。
  • 解锁:执行 monitorexit 时,释放锁,唤醒 EntryList 中的线程竞争锁。
  • 特性:天生支持可重入性(同一线程可多次加锁)和内存可见性(解锁时同步变量到主存)。
(3)锁升级过程(JDK 1.6 优化)

为适应不同竞争强度,synchronized 会自动升级锁状态,降低开销:

  1. 无锁:初始状态,无线程竞争。
  2. 偏向锁:单线程多次加锁时,通过 CAS 把线程 ID 写入 Mark Word,后续线程直接对比 ID 即可进入(无 CAS 开销)。
  3. 轻量级锁:有轻微竞争(如两个线程竞争),撤销偏向锁后,线程通过 CAS 自旋(用户态)竞争锁(避免内核态阻塞)。
  4. 重量级锁:竞争激烈(自旋超时或线程过多),锁膨胀为 Monitor 锁,未抢到锁的线程进入内核态阻塞(避免 CPU 空转)。

2. ReentrantLock:并发包可重入锁

(1)核心特性
  • 实现 Lock 接口,是可重入锁,支持公平锁 / 非公平锁超时获取锁中断锁等高级特性(比 synchronized 灵活)。
  • 默认是非公平锁,可通过构造函数 new ReentrantLock(true) 指定为公平锁。
(2)底层实现:AQS 框架

ReentrantLock 的核心依赖 AQS(AbstractQueuedSynchronizer) 框架,AQS 由两部分组成:

  • state 变量:表示锁的状态(独占模式):
    • state = 0:无锁状态。
    • state = 1:已被加锁。
    • state > 1:可重入(同一线程多次加锁)。
  • FIFO 双向等待队列:未抢到锁的线程被封装为 Node 节点,加入队列,锁释放时唤醒队首节点。
(3)公平锁与非公平锁的实现
  • 公平锁:线程竞争锁时,先检查等待队列是否有线程排队,若有则加入队尾,按顺序竞争。
  • 非公平锁:线程竞争锁时,先尝试 CAS 抢占锁,抢占失败再加入队列(允许 “插队”,性能更高,默认选择)。

3. synchronized 与 ReentrantLock 对比

特性synchronizedReentrantLock
锁实现JVM 内置 Monitor 锁AQS 框架(Java 代码实现)
公平性非公平锁(无法指定)支持公平 / 非公平(默认非公平)
高级特性无(不可中断、无超时)可中断、超时获取、尝试锁
可重入性支持支持
锁升级自动升级(无锁→偏向→轻量→重量)无自动升级,需手动控制
性能低竞争时与 ReentrantLock 接近高竞争时性能更稳定

六、并发集合:线程安全的容器

1. 线程安全集合分类

(1)早期线程安全集合(不推荐)
  • Vector:ArrayList 的线程安全版本,方法加 synchronized(全局锁,性能低)。
  • Hashtable:HashMap 的线程安全版本,方法加 synchronized(全局锁,性能低)。
(2)推荐的线程安全集合(java.util.concurrent 包)
  • ConcurrentHashMap:高性能并发哈希表(重点)。
  • CopyOnWriteArrayList:读多写少场景的线程安全 List。
  • ConcurrentSkipListMap:有序并发 Map(基于跳表实现)。
  • 包装类:Collections.synchronizedList(List)Collections.synchronizedMap(Map)(对普通集合加锁包装,性能一般)。

2. 重点集合原理

(1)CopyOnWriteArrayList:写时复制
  • 实现原理:
    1. 内部基于数组存储,写操作(add/remove)时复制新数组,在新数组上操作。
    2. 写操作加锁(避免并发写入覆盖),读操作无锁(读原数组)。
    3. 写操作完成后,原数组引用指向新数组。
  • 优缺点:
    • 优点:读性能极高,无锁竞争,适合读多写少场景。
    • 缺点:内存占用高(复制数组),读不到实时数据(最终一致性)。
(2)ConcurrentHashMap:高效并发哈希表

核心优化:锁粒度细化,避免全局锁,Java 7 和 8 实现不同:

  • Java 7:分段锁(Segment),将数组分为多个 Segment,每个 Segment 是独立的 ReentrantLock,锁粒度是 Segment。
  • Java 8+:取消 Segment,采用CAS + synchronized
    • CAS 用于初始化数组、头节点(短耗时操作)。
    • synchronized 锁住链表 / 红黑树的头节点(锁粒度是单个桶)。
  • 优势:并发度更高,冲突概率更低,性能优于 Hashtable。

3. 常见问题

(1)ArrayList 线程安全吗?如何解决?
  • 不安全:高并发 add 时,会出现元素为 null、索引越界、size 与实际元素数不符。
  • 解决方案:
    1. Collections.synchronizedList(new ArrayList<>()) 包装。
    2. 替换为 CopyOnWriteArrayList

六、CAS 与乐观锁 / 悲观锁

1. CAS:无锁并发的核心

(1)定义与原理

CAS(Compare And Swap):比较并交换,是乐观锁的核心实现,无锁机制,通过硬件指令保证原子性。

  • 核心逻辑:修改共享变量前,先比较 “预期旧值” 与 “主存当前值”:
    1. 若一致:说明未被其他线程修改,将新值写入主存。
    2. 若不一致:说明被修改,自旋重试(重新读取旧值,再次尝试)。
  • 流程:主存变量 V → 线程工作内存拷贝 V 为 A → 计算新值 B → CAS 比较 V 与 A,一致则 V=B,否则重试。
(2)应用场景与优缺点
  • 应用:AQS 框架、AtomicInteger 等原子类(如 atomicInteger.incrementAndGet())。
  • 优点:无锁,避免线程阻塞切换开销,并发性能高。
  • 缺点:自旋会占用 CPU(高竞争场景下自旋频繁,CPU 负载高)。

2. 乐观锁与悲观锁

类型核心思想实现方式适用场景并发性能
乐观锁认为并发冲突概率低,提交时检查CAS、版本号法读多写少、冲突少(如查询)
悲观锁认为并发冲突概率高,操作时加锁synchronized、ReentrantLock写多读少、冲突多(如支付)

七、核心知识点总结

  1. 线程创建:4 种方式,线程池优先;线程状态:6 种,核心流转路径需牢记。
  2. 线程安全本质:原子性、可见性、有序性,需通过锁或无锁机制保证。
  3. ThreadLocal:线程隔离,核心是 ThreadLocalMap,必须手动 remove 避免内存泄漏。
  4. 锁机制:synchronized 是 JVM 内置锁(自动升级),ReentrantLock 是 API 锁(灵活高级)。
  5. 并发集合:CopyOnWriteArrayList 适合读多写少,ConcurrentHashMap 是高效并发哈希表。
  6. CAS:无锁核心,乐观锁实现,适用于短耗时操作;悲观锁适用于高冲突场景。

以上是 JUC 核心知识点的浓缩,复习时可结合代码案例强化理解,重点关注线程安全、锁机制、内存泄漏等高频考点。

http://www.dtcms.com/a/499432.html

相关文章:

  • 网站上海备案大渡口发布
  • 虚幻引擎入门教程:虚幻编辑器的基本操作
  • 建行网站首页登录网上银行百度服务中心官网
  • chrome 浏览器更新
  • 单元测试、系统测试、集成测试知识总结
  • 【C++/Lua联合开发】 (三) C++调用Lua
  • 网站开发与黑客做网站维护需要懂什么
  • C++动态规划——LIS(最长不下降子序列)
  • 计算 CIDR 块包含 C 类地址数量的方法
  • [创业之路-702]:“第三次”与“第四次工业革命”的范式跃迁
  • php 简单购物网站diy定制网站
  • 《vector.pdf 深度解读:vector 核心接口、扩容机制与迭代器失效解决方案》
  • Linux中slab缓存初始化kmem_cache_init函数和定时回收函数的实现
  • 南头专业的网站建设公司厦门网站建设公司怎么选
  • 郑州市做网站的公司西安有什么好玩的地方吗
  • Java 大视界 -- 金融市场情绪预测与动态决策的 Java 大数据实战(2024 券商落地版 425)
  • 运维干货:Nginx 常用配置与问题排查指南
  • 条款16:保证const成员函数的线程安全性
  • 网站开发需求现在网站怎么备案
  • 巧用LEF实现row aware track规划
  • 大话数据结构之 <栈> 和<队列>(C语言)
  • Windows 系统的 Delivery Optimization后台用了几GB流量,如何暂停?
  • 基于ads1256的ADC控制实现
  • 建站之星破解版手机正规建网站企业
  • 建一个电商网站要多少钱wordpress及时聊天
  • 云端思维导图软件,多设备同步无压力
  • Python Web 开发:从框架到实战案例
  • 做网站每天任务及实训过程公司关于网站建设的通知
  • 网站联系方式修改织梦网站建设是在商标哪个类别
  • 网站管理员密码在哪里找个人做网站的