JUC 八股文面试题
谈谈你对线程和进程的理解
| 进程 | 线程 |
|---|---|
| 操作系统资源分配的基本单位 | CPU 调度的基本单位 |
一个进程可包含多个线程
创建线程有几种方式?分别适用什么场景?
- 继承 Thread 类,重写run()方法:在无需返回值时使用
- 实现 Runnable 接口,实现run()方法:在需要返回值时使用
- 实现 Callable 接口 + Future,实现call()方法:在需要抛出异常时使用
线程有哪些状态?
新建(New):线程刚创建,未调用start()。
就绪(Runnable):调用start()后,等待 CPU 调度(包含 “运行中” 状态)。
阻塞(Blocked):等待 synchronized 锁释放时进入。
等待(Waiting):调用wait()、join()、park()等,需被唤醒(无超时)。
超时等待(Timed Waiting):调用wait(time)、sleep(time)等,超时后自动唤醒。
终止(Terminated):run()执行完毕或异常终止。

什么是线程上下文切换?
当 CPU 从一个线程切换到另一个线程时,需保存当前线程的状态并加载新线程的状态,这个过程称为上下文切换(CPU将自己的时间片分给不同的的线程来执行任务)
频繁切换会消耗 CPU 资源,影响性能
什么是线程安全问题?
多个线程并发访问共享资源时,因执行顺序不确定(如指令重排、线程调度差异),导致最终结果与预期不符的问题。
导致线程不安全的原因主要有三个:原子性、可见性、有序性。
什么是原子性问题?
原子性指一个或多个操作要么全部执行,要么全部不执行,非原子操作在多线程下可能被打断,导致数据不一致。
解决方式:加锁
synchronized的加锁范围有哪些?分别锁的是什么对象?
修饰实例方法:锁当前对象(this),同一对象的多个同步方法互斥。
修饰静态方法:锁当前类的 Class 对象(如Xxx.class),所有实例共享该锁。
修饰代码块:锁括号中的对象(如synchronized(obj) { … }),可指定任意对象作为锁。
什么是死锁问题?如何检测死锁问题?如何避免死锁?
死锁问题:两个或者两个以上的线程持有资源,并且互相等待对方手里资源的这一种现象叫做死锁
排查死锁:
- 使用jps+jstack命令
- 使用arthas
避免死锁:
- 顺序加锁
- 设置锁超时时间
synchronized锁升级是怎么样的?
无锁:初始状态,无线程竞争。
偏向锁:第一个线程获取锁,记录线程id,此时为偏向锁。
轻量级锁:出现多个线程获取锁,但不发生实际竞争时(交替加锁,不在同一时刻竞争同一把锁),升级为轻量级锁。
重量级锁:发生实际竞争时,升级为重量级锁。
什么是悲观锁和乐观锁?都有哪些具体的实现?
悲观锁:认为竞争必然发生,操作前就先加锁,如synchronized、ReentrantLock。
乐观锁:认为竞争少,先操作,操作完后进行比较,发生冲突重试,如CAS。
什么是CAS?底层是基于什么实现的?
CAS (Compare-And-Swap、比较并且交换) 。
Java底层实际是调用Unsafe类提供的native方法实现的,底层通过CPU保证原子性。
什么是ABA问题?如何解决?
问题:变量值从 A→B→A,CAS 会误认为值未变,导致错误更新。
解决:引入版本号,更新时版本号+1
什么是可见性问题?怎么能解决可见性问题?
问题:一个线程修改共享变量后,其他线程不能立即看到最新值
解决:
- 对变量加
volatile修饰 - 加
synchronized锁
什么是有序性问题?怎么能解决有序性问题?
问题:因指令重排导致的程序执行顺序与代码顺序不一致,进而导致多线程逻辑错误
解决:
- 对变量加
volatile修饰 - 加
synchronized锁
volatile如何保证可见性以及有序性?能保证原子性吗?
volatile通过内存屏障保证可见性和有序性,但不能保证原子性,原子性可以通过加锁保证。
volatile和synchronized有什么区别?
volatile:
- 可以保证可见性和有序性,但不能保证原子性;
- 加在变量上;
- 性能开销影响较小。
synchronized:
- 可见性、有序性、原子性都能保证;
- 加在方法或代码块上;
- 性能开销影响大。
sleep(long n) 和 wait(long n) 有什么区别?
| 特性 | sleep(long n) | wait(long n) |
|---|---|---|
| 所属类 | Thread | Object |
| 锁释放 | 不释放锁 | 释放锁 |
| 唤醒方式 | 超时自动唤醒 | 超时或被 notify 唤醒 |
| 使用场景 | 暂停执行(如定时) | 线程间等待通知 |
如何创建线程池?线程池的7个参数分别是什么,作用分别是什么?
通过ThreadPoolExecutor构造函数创建,核心参数:
- corePoolSize:核心线程数(长期存活,即使空闲)。
- maximumPoolSize:最大线程数(核心 + 非核心线程的上限)。
- keepAliveTime:非核心线程的空闲存活时间。
- unit:
keepAliveTime的时间单位(如TimeUnit.SECONDS)。 - workQueue:任务等待队列(核心线程满时存放任务)。
- threadFactory:创建线程的工厂(可自定义线程名、优先级等)。
- handler:拒绝策略(任务满时如何处理新任务)。
为什么不推荐使用 Executors 工具类创建线程池?
都会导致OOM
(FixedThreadPool/SingleThreadPool:使用无界队列(LinkedBlockingQueue);CachedThreadPool:最大线程数为Integer.MAX_VALUE)
任务提交到线程池后,执行流程是怎么样的?
- 提交任务,若核心线程数未满,创建核心线程执行任务。
- 核心线程满,任务加入等待队列。
- 等待队列满,若未达最大线程数,创建非核心线程执行任务。
- 达到最大线程数,执行拒绝策略。
线程池的拒绝策略有哪些,默认是哪一种?
直接抛异常(默认) / 直接丢弃 / 丢弃最老的任务 / 交由提交任务的线程执行
- AbortPolicy(默认):抛
RejectedExecutionException异常。 - CallerRunsPolicy:让提交任务的线程自己执行(减缓提交速度,适用于轻量任务)。
- DiscardPolicy:直接丢弃新任务(无提示)。
- DiscardOldestPolicy:丢弃队列中最旧的任务,再尝试提交新任务。
ThreadLocalMap是什么?其Key和Value分别是什么?
ThreadLocal的静态内部类,类似 HashMap(但结构更简单),用于存储线程本地变量。
- Key:
ThreadLocal对象(弱引用,避免 ThreadLocal 对象未被引用时的内存泄漏)。 - Value:线程私有变量(强引用)。
为什么执行了ThreadLocal的set方法后要执行remove方法?不执行会有什么后果吗?
ThreadLocalMap 的 Key(ThreadLocal 对象)被设计为弱引用,当该 ThreadLocal 对象失去所有外部强引用后,垃圾回收时会回收 Key。此时 Key 变为 null,但对应的 Value 因被 Entry 强引用而无法释放。若线程长期存活(如线程池场景)且未主动调用 remove() 清理,残留的 Value 会持续累积,最终导致内存泄漏甚至内存溢出(OOM)
什么是AQS?有哪三个核心?
AQS(AbstractQueuedSynchronizer):抽象队列同步器,是 Java 并发工具(如 ReentrantLock、Semaphore)的基础框架。
核心:state变量,CLH队列(双向链表,先进先出),CAS操作
ReentrantLock 锁和 synchronized 锁有什么区别?
synchronized 是JVM级别的锁,但是 ReentrantLock 是Java 层面的锁(底层是基于AQS实现的)
中公超多
| ReentrantLock | synchronized | |
|---|---|---|
| 中断 | 可中断 | 不可中断 |
| 公平 | 支持公平和非公平 | 只有非公平 |
| 超时 | 指定时间未获取到锁就不继续获取 | 一直等待 |
| 多个条件变量 | 有多个waitSet | 只有一个waitSet |
Semaphore有什么特点?适用于什么场景?底层是怎么实现的?
- 特点:控制同时访问资源的线程数(基于 “许可” 机制)。
- 场景:限流(如限制并发请求数)、资源池控制(如限制数据库连接数)。
- 底层实现:基于 AQS(state 表示可用许可数。
acquire()减少许可,release()增加许可)
CountdownLatch有什么特点?适用于什么场景?底层是怎么实现的?
- 特点:让主线程等待多个子线程完成后再执行(倒计时门闩)。
- 场景:线程同步协作,主线程等待所有任务线程初始化完成后再开始工作。
- 底层实现:基于 AQS(state 为初始计数。子线程调用
countDown()减 1,主线程调用await()等待 state=0)
CyclicBarrier有什么特点?适用于什么场景?
- 特点:让多个线程到达 “屏障点” 后再一起继续执行(可重用)。
- 场景:分阶段任务(如多线程分块处理数据,所有线程完成后再汇总)。




