Java多线程基础核心考点

在Java开发领域,多线程是衡量开发者并发编程能力的核心指标。无论是银行系统的高并发交易,还是电商平台的秒杀场景,多线程技术都是不可或缺的基石。本站整理的Java多线程面试50题,并非简单的知识点罗列,而是基于真实面试场景,从基础概念到高阶应用的全方位解析。

① 进程与线程的区别

进程是资源分配的最小单位,而线程是CPU调度的最小单位。每个进程拥有独立的内存空间,而同一进程内的线程共享堆和方法区,但拥有独立的栈空间。理解这一区别是掌握Java多线程的前提。

② 线程的生命周期

Java线程有六种状态:NEW(新建)、RUNNABLE(运行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(超时等待)、TERMINATED(终止)。面试中常考状态转换的条件,如wait()进入等待,notify()唤醒等。

③ start()与run()的区别

调用start()方法会启动新线程并执行run()方法,而直接调用run()方法只是普通的方法调用,不会启动新线程。这是多线程面试中最基础也最容易混淆的考点之一。

线程创建的四种方式

在Java中,创建线程主要有以下几种方式,每种方式都有其适用场景:


// 示例:实现Runnable接口创建线程
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程执行中...");
    }
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start(); // 启动线程
    }
}
        

JUC包深入解析

java.util.concurrent(JUC)包是Java并发编程的核心,提供了丰富的并发工具类。在Java多线程面试50题中,JUC相关知识点占据了相当大的比重,尤其是ReentrantLock、ReadWriteLock、Semaphore等类的使用场景和底层原理。

ReentrantLock详解

ReentrantLock是可重入锁,实现了Lock接口。与synchronized相比,它提供了更灵活的锁机制,如尝试获取锁、定时获取锁、公平锁等。其底层基于AQS(AbstractQueuedSynchronizer)实现。

  • 可重入性:同一个线程可以多次获取同一把锁,不会发生死锁。
  • 公平锁与非公平锁:公平锁按等待时间分配,非公平锁可能插队,性能更高。
  • 条件变量:通过newCondition()创建,实现线程间的精确通知。

ReadWriteLock读写锁

ReadWriteLock维护了一对锁,一个读锁和一个写锁。读锁是共享锁,写锁是独占锁。在读多写少的场景下,读写锁能显著提升性能。

常见实现类:ReentrantReadWriteLock。面试中常问:读锁和写锁的兼容性?答:读-读不互斥,读-写互斥,写-写互斥。

Semaphore信号量

Semaphore用于控制同时访问特定资源的线程数量。常用于限流、资源池管理等场景。例如,限制数据库连接池的最大连接数。

Semaphore semaphore = new Semaphore(5); // 允许5个线程同时访问
semaphore.acquire(); // 获取许可
try {
    // 访问资源
} finally {
    semaphore.release(); // 释放许可
}
                

synchronized锁机制深度剖析

synchronized是Java内置的同步关键字,其底层实现基于JVM的monitor对象。在Java多线程面试50题中,synchronized的锁升级过程、性能优化及与ReentrantLock的区别是高频考点。

锁升级过程

JDK 1.6之后,synchronized进行了大量优化,引入了锁升级机制:

① 无锁状态

对象刚创建时,处于无锁状态,Mark Word中存储对象的哈希码、分代年龄等信息。

② 偏向锁

当第一个线程获取锁时,对象Mark Word中记录线程ID,后续该线程再次获取锁无需同步。

③ 轻量级锁

当有其他线程尝试获取锁时,偏向锁升级为轻量级锁,通过CAS操作竞争锁,自旋等待。

④ 重量级锁

当竞争激烈时,轻量级锁升级为重量级锁,线程阻塞,等待OS调度,性能开销较大。

synchronized与ReentrantLock的区别

特性 synchronized ReentrantLock
实现层面 JVM层面 JDK层面(AQS)
锁释放 自动释放 需手动释放(finally中)
公平锁 非公平锁 可配置公平/非公平
条件变量 wait/notify Condition接口
中断响应 不支持 支持

线程池核心参数与最佳实践

线程池是Java多线程面试50题中的重中之重。合理配置线程池能显著提升系统性能和稳定性。ThreadPoolExecutor是核心类,其构造函数包含7个关键参数。

线程池七大参数

  1. corePoolSize:核心线程数,线程池中长期存活的线程数量。
  2. maximumPoolSize:最大线程数,线程池允许创建的最大线程数。
  3. keepAliveTime:空闲线程存活时间,非核心线程空闲超过此时间将被回收。
  4. unit:keepAliveTime的时间单位。
  5. workQueue:任务队列,存放等待执行的任务。
  6. threadFactory:线程工厂,用于创建新线程,可自定义线程名等。
  7. handler:拒绝策略,当队列满且线程数达最大值时,如何处理新任务。

常见拒绝策略

线程池配置建议

线程池大小的配置需根据任务类型决定:

volatile关键字与内存模型

volatile是Java虚拟机提供的轻量级同步机制,在Java多线程面试50题中,volatile的可见性和有序性保证是核心考点。

volatile的三大特性

① 可见性

当一个线程修改了volatile变量的值,新值对其他线程立即可见。实现原理是通过内存屏障和缓存一致性协议(MESI)。

② 有序性

volatile禁止指令重排序,保证代码执行顺序与代码书写顺序一致。通过插入内存屏障实现。

③ 不保证原子性

volatile不保证复合操作的原子性,如i++。需结合synchronized或AtomicInteger使用。

内存屏障

volatile通过插入内存屏障禁止指令重排序:

死锁的产生与排查

死锁是多线程开发中的常见陷阱,在Java多线程面试50题中,死锁的产生条件、预防和排查方法是必考内容。

死锁产生的四个必要条件

  1. 互斥条件:资源一次只能被一个线程占用。
  2. 占有并等待:线程持有资源的同时等待其他资源。
  3. 不可抢占:资源只能由持有者主动释放。
  4. 循环等待:线程形成头尾相接的循环等待资源关系。

死锁排查工具

预防死锁的策略