谈谈你对线程和进程的理解

进程 线程
操作系统资源分配的基本单位 CPU 调度的基本单位

一个进程可包含多个线程

创建线程有几种方式?分别适用什么场景?

  1. 继承 Thread 类,重写run()方法:在无需返回值时使用
  2. 实现 Runnable 接口,实现run()方法:在需要返回值时使用
  3. 实现 Callable 接口 + Future,实现call()方法:在需要抛出异常时使用

线程有哪些状态?

新建(New):线程刚创建,未调用start()。
就绪(Runnable):调用start()后,等待 CPU 调度(包含 “运行中” 状态)。
阻塞(Blocked):等待 synchronized 锁释放时进入。
等待(Waiting):调用wait()、join()、park()等,需被唤醒(无超时)。
超时等待(Timed Waiting):调用wait(time)、sleep(time)等,超时后自动唤醒。
终止(Terminated):run()执行完毕或异常终止。

image-20251009165542168.png

什么是线程上下文切换?

当 CPU 从一个线程切换到另一个线程时,需保存当前线程的状态并加载新线程的状态,这个过程称为上下文切换(CPU将自己的时间片分给不同的的线程来执行任务)

频繁切换会消耗 CPU 资源,影响性能

什么是线程安全问题?

多个线程并发访问共享资源时,因执行顺序不确定(如指令重排、线程调度差异),导致最终结果与预期不符的问题。

导致线程不安全的原因主要有三个:原子性、可见性、有序性。

什么是原子性问题?

原子性指一个或多个操作要么全部执行,要么全部不执行,非原子操作在多线程下可能被打断,导致数据不一致。

解决方式:加锁

synchronized的加锁范围有哪些?分别锁的是什么对象?

修饰实例方法:锁当前对象(this),同一对象的多个同步方法互斥。
修饰静态方法:锁当前类的 Class 对象(如Xxx.class),所有实例共享该锁。
修饰代码块:锁括号中的对象(如synchronized(obj) { … }),可指定任意对象作为锁。

什么是死锁问题?如何检测死锁问题?如何避免死锁?

  • 死锁问题:两个或者两个以上的线程持有资源,并且互相等待对方手里资源的这一种现象叫做死锁

  • 排查死锁:

    1. 使用jps+jstack命令
    2. 使用arthas
  • 避免死锁:

    1. 顺序加锁
    2. 设置锁超时时间

synchronized锁升级是怎么样的?

无锁:初始状态,无线程竞争。

偏向锁:第一个线程获取锁,记录线程id,此时为偏向锁。

轻量级锁:出现多个线程获取锁,但不发生实际竞争时(交替加锁,不在同一时刻竞争同一把锁),升级为轻量级锁。

重量级锁:发生实际竞争时,升级为重量级锁。

什么是悲观锁和乐观锁?都有哪些具体的实现?

悲观锁:认为竞争必然发生,操作前就先加锁,如synchronized、ReentrantLock。

乐观锁:认为竞争少,先操作,操作完后进行比较,发生冲突重试,如CAS。

什么是CAS?底层是基于什么实现的?

CAS (Compare-And-Swap、比较并且交换) 。

Java底层实际是调用Unsafe类提供的native方法实现的,底层通过CPU保证原子性。

什么是ABA问题?如何解决?

问题:变量值从 A→B→A,CAS 会误认为值未变,导致错误更新。

解决:引入版本号,更新时版本号+1

什么是可见性问题?怎么能解决可见性问题?

问题:一个线程修改共享变量后,其他线程不能立即看到最新值

解决:

  1. 对变量加volatile修饰
  2. 加synchronized锁

什么是有序性问题?怎么能解决有序性问题?

问题:因指令重排导致的程序执行顺序与代码顺序不一致,进而导致多线程逻辑错误

解决:

  1. 对变量加volatile修饰
  2. 加synchronized锁

volatile如何保证可见性以及有序性?能保证原子性吗?

volatile通过内存屏障保证可见性和有序性,但不能保证原子性,原子性可以通过加锁保证。

volatile和synchronized有什么区别?

  • volatile:

    1. 可以保证可见性和有序性,但不能保证原子性;
    2. 加在变量上;
    3. 性能开销影响较小。
  • synchronized:

    1. 可见性、有序性、原子性都能保证;
    2. 加在方法或代码块上;
    3. 性能开销影响大。

sleep(long n) 和 wait(long n) 有什么区别?

特性 sleep(long n) wait(long n)
所属类 Thread Object
锁释放 不释放锁 释放锁
唤醒方式 超时自动唤醒 超时或被 notify 唤醒
使用场景 暂停执行(如定时) 线程间等待通知

如何创建线程池?线程池的7个参数分别是什么,作用分别是什么?

通过ThreadPoolExecutor构造函数创建,核心参数:

  1. corePoolSize:核心线程数(长期存活,即使空闲)。
  2. maximumPoolSize:最大线程数(核心 + 非核心线程的上限)。
  3. keepAliveTime:非核心线程的空闲存活时间。
  4. unit:keepAliveTime的时间单位(如TimeUnit.SECONDS)。
  5. workQueue:任务等待队列(核心线程满时存放任务)。
  6. threadFactory:创建线程的工厂(可自定义线程名、优先级等)。
  7. handler:拒绝策略(任务满时如何处理新任务)。

为什么不推荐使用 Executors 工具类创建线程池?

都会导致OOM

(FixedThreadPool/SingleThreadPool:使用无界队列(LinkedBlockingQueue);CachedThreadPool:最大线程数为Integer.MAX_VALUE)

任务提交到线程池后,执行流程是怎么样的?

  1. 提交任务,若核心线程数未满,创建核心线程执行任务。
  2. 核心线程满,任务加入等待队列。
  3. 等待队列满,若未达最大线程数,创建非核心线程执行任务。
  4. 达到最大线程数,执行拒绝策略。

线程池的拒绝策略有哪些,默认是哪一种?

直接抛异常(默认) / 直接丢弃 / 丢弃最老的任务 / 交由提交任务的线程执行

  • 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有什么特点?适用于什么场景?

  • 特点:让多个线程到达 “屏障点” 后再一起继续执行(可重用)。
  • 场景:分阶段任务(如多线程分块处理数据,所有线程完成后再汇总)。