Java 多线程
约 1561 字大约 5 分钟
2026-05-18
Q: 线程的生命周期和状态
6 个状态:
- NEW: 初始态,刚创建
- RUNNABLE 运行态,
start()启动后,包含:READY: 等待态,等待 CPU 时间 - BLOCKED: 阻塞状态,获取锁时发现锁已经被占用了,阻塞等待锁,已有的锁不释放
- WAITING: 等待状态,主动进入,释放锁,等待唤醒。
- TIME_WAITING: 超时等待状态,到时间自动返回。
- TERMINATED: 终止
Q: 什么是死锁?如何产生?
多个线程同时阻塞,都在等待某个资源被释放。无限阻塞,无法释放。
产生的必要条件:
- 互斥条件:资源在任意时刻只能被一个线程占有。
- 持有并等待:线程已持有至少一个资源,同时等待其他被占有的资源。
- 不可剥夺:线程的资源无法被其他线程强行剥夺,必须自己释放。
- 循环等待:多个线程形成资源请求循环
Q: 如何检测和预防死锁?
检测:使用 jstack <pid> 命令、JConsole 、VisualVM 等工具,也可配合 top 等系统命令查看。
预防:破坏死锁产生的必要条件即可。
- 破坏 互斥条件:用无锁结构或者读写锁。(现实很难消除)
- 破坏 持有并等待:一次性申请所有需要的锁。(现实情况比较难)
- 破坏 不可剥夺:用可超时、可中断的锁。
- 破坏 循环等待:按照固定顺序加锁。
Q: 线程创建方式有几种?
5 种。继承 Thread、实现 Runnable、实现 Callable + FutureTask、线程池、CompleableFuture
可见:线程创建方式
Q: 说一下 Java 内存模型
JMM 是一种规则,解决多线程直接如何正确看到共享变量的修改。
特性:
- 可见性:A 线程修改变量,B 如何看到?可以用
volatile或者synchronized - 有序性:重排问题怎么办?可以用
volatile或者synchronized禁止某些重排。 - 原子性:如
count++非原子操作,需要synchronized或者 Atomic 原子类解决。
Q: volatile 关键字怎么保证变量可见性?
标注了 volatile 关键字的变量,会指示 JVM 每次读取内容时,都必须从主内存中获取
Q: 什么是乐观锁和悲观锁?
乐观锁:假设冲突少,只在更新时才检查,适合读多写少。
悲观锁:假设冲突多,操作前加锁,适合读少写多。
Q: 什么是 CAS 算法?
CAS (compare and swap) 是一种原子操作,比较当前内存值和预期值是否相等来决定是否更新,常用乐观锁的实现。
Q: synchronized 是什么,有什么用?
synchronized 关键字是 Java 内置悲观锁机制,通过加锁确保同一时刻只有一个线程执行临界区代码,实现线程安全。
Q: ReentrantLock 是什么?
ReentrantLock 是 Java 并发包内的一个可重入互斥锁,实现了 Lock 接口,具备与 synchronized 相同的基本行为和语义,但提供了更加丰富的功能和灵活的控制。
具体请看:可重入锁
Q: 公平锁和非公平锁有什么区别?
首先,Java 并发中,公平锁和非公平锁,是 ReentrantLock 可重入锁的两种实现,主要区别在于线程获取锁的顺序,以及带来的性能差异和适用场景。
公平锁按照锁的请求时间顺序,分配锁。
非公平锁允许“插队”的行为发生。
| 公平锁 | 非公平锁 | |
|---|---|---|
| 获取顺序 | 请求时间顺序 | 允许“插队”的行为发生。 |
| 实现方式 | 请求锁的线程会进入等待队列,锁释放后只能从队列头部唤醒线程。 | 线程获取锁的时候,如果锁可用,就直接获取,否则就进入等待队列。 |
| 吞吐量 | 较低,需要频繁挂起和唤醒锁,维护队列 | 较高 |
| 可能出现的问题 | 无饥饿现象 | 可能产生饥饿,部分线程长时间得不到锁。 |
| 场景 | 锁竞争激烈、要求按顺序 | 竞争不激烈的时候 |
提示
syncronized 是非公平锁。
Q: ReentrantLock 和 synchronized 有什么区别?
| ReentrantLock | synchronized | |
|---|---|---|
| 中断锁获取 | 支持 | 不支持 |
| 带超时 | 支持 | 不支持 |
| 公平锁 | 支持 | 不支持 |
| 释放锁 | 支持手动释放 | 自动释放锁 |
Q: Atomic 原子类了解吗?
多线程环境下,若多个线程需要并发的更新共享变量,就需要同步手段防止竞争,以前用 sychronized 重量级锁 或者 灵活的 ReentrantLock ,在有时候,我们只是对一个数值进行简单的加减、如果用锁就太重了。
这时候就用原子类,利用底层的 CAS 机制,避免加锁,提高性能。
常见的类包含:AtomicInteger、AtomicBoolean
(AtomicInteger为例)使用方法一般是:get()/set() 、incrementAndGet()/getAndIncrement()
Q: 线程池架构是什么?
线程池分 2 大类:
ExcutorService,有四种创建方式:固定大小、自动扩容、单线程池、定时任务线程池ThreadPoolExecutor,自定义线程池,重要的是参数- 参数:核心线程数、最大线程数、非核心线程最大空闲时间、时间单位、任务队列、线程工厂、拒绝策略
Executors.newFixedThreadPool(n); // 固定大小
Executors.newCachedThreadPool(); // 自动扩容
Executors.newSingleThreadPool(); // 单线程池
Executors.newScheduledThreadPool(n); // 定时任务线程池Q: 介绍一下 ThreadPoolExecutor 类的参数。
参数分别是:核心线程数、最大线程数、非核心线程最大空闲时间、时间单位、线程工厂、拒绝策略。
参数重点还是:线程池的的排队与任务调度机制、拒绝策略(下一个问题)
提交过程:
- 核心线程数未满 ➜ 创建新的线程去完成任务
- 核心线程数满了,队列未满 ➜ 任务入队
- 队列也满了,线程数未到最大值 ➜ 创建非核心线程处理任务
- 队列满了,线程数也满了 ➜ 拒绝策略
Q: 拒绝策略有哪些?应用场景是什么?
4 种
AbortPolicy默认:抛出异常CallerRunsPolicy,会退给提交线程DiscardPolicy,丢弃任务DiscardOldestPolicy丢弃最早的任务,执行最新的任务。
版权所有
版权归属:FelixJY
