deepseek梳理java高级开发工程师多线程面试题

Java多线程全面面试题及答案

一、Java线程基础

1. 线程创建与生命周期

Q1: Java中创建线程有哪几种方式?各有什么优缺点?

答案:

1. 继承Thread类:
   - 优点:简单直接
   - 缺点:Java单继承限制,不利于扩展
   - 示例:
     class MyThread extends Thread {
         public void run() {
             // 线程执行逻辑
         }
     }

2. 实现Runnable接口:
   - 优点:避免单继承限制,更灵活
   - 缺点:无法直接获取执行结果
   - 示例:
     class MyRunnable implements Runnable {
         public void run() {
             // 线程执行逻辑
         }
     }

3. 实现Callable接口:
   - 优点:可以获取返回值,支持异常抛出
   - 缺点:使用稍复杂
   - 示例:
     class MyCallable implements Callable<String> {
         public String call() throws Exception {
             return "执行结果";
         }
     }

4. 线程池创建:
   - 优点:资源复用,管理方便
   - 缺点:配置参数需要调优
   - 示例:
     ExecutorService executor = Executors.newFixedThreadPool(5);
     executor.submit(() -> {...});

Q2: 详细描述线程的6种状态及转换条件

答案:

1. NEW(新建):
   - 线程被创建但未调用start()
   
2. RUNNABLE(可运行):
   - 调用start()后进入该状态
   - 包含就绪(Ready)和运行中(Running)两种子状态
   
3. BLOCKED(阻塞):
   - 等待获取监视器锁(synchronized)
   - 只有其他线程释放锁后才能转换
   
4. WAITING(无限等待):
   - 调用wait()/join()/LockSupport.park()
   - 需要其他线程notify()/notifyAll()/unpark()
   
5. TIMED_WAITING(定时等待):
   - 调用sleep(time)/wait(time)/join(time)
   - 超时或收到通知后转换
   
6. TERMINATED(终止):
   - 线程执行完毕或异常退出

状态转换图:
NEW → start() → RUNNABLE
RUNNABLE → 获取锁失败 → BLOCKED
RUNNABLE → wait() → WAITING
WAITING → notify() → BLOCKED
RUNNABLE → sleep(time) → TIMED_WAITING
TIMED_WAITING → 超时 → RUNNABLE
RUNNABLE → 执行结束 → TERMINATED

2. 线程基本操作

Q3: sleep()、wait()、yield()和join()方法的区别?

答案:

┌──────────┬────────────┬────────────┬────────────┬────────────┐
│ 方法      │ 所属类      │ 释放锁      │ 唤醒条件     │ 使用场景     │
├──────────┼────────────┼────────────┼────────────┼────────────┤
│ sleep()  │ Thread     │ 不释放      │ 时间到期     │ 定时暂停     │
│ wait()   │ Object     │ 释放        │ notify()    │ 线程间协作   │
│ yield()  │ Thread     │ 不释放      │ 系统调度     │ 让出CPU      │
│ join()   │ Thread     │ -          │ 线程结束     │ 等待线程完成 │
└──────────┴────────────┴────────────┴────────────┴────────────┘

关键区别:
1. wait()必须在同步块中调用,sleep()可以在任何地方
2. wait()会释放对象锁,sleep()不会释放任何锁
3. yield()只是建议调度器让出CPU,不保证一定生效
4. join()底层是通过wait()实现的

二、线程安全与锁机制

1. synchronized关键字

Q4: synchronized的实现原理是什么?JDK1.6做了哪些优化?

答案:

实现原理:
1. 同步代码块:
   - 使用monitorenter/monitorexit字节码指令
   - 每个对象关联一个Monitor对象
2. 同步方法:
   - 方法标志位添加ACC_SYNCHRONIZED
   - 隐式使用当前对象的Monitor

JDK1.6优化:
1. 锁升级机制:
   无锁 → 偏向锁 → 轻量级锁 → 重量级锁
2. 新增锁状态:
   - 偏向锁:消除无竞争时的同步开销
   - 轻量级锁:通过CAS避免阻塞
3. 适应性自旋:
   根据上次自旋结果动态调整自旋次数
4. 锁粗化:
   合并连续同步块减少锁操作

2. volatile关键字

Q5: volatile的作用和实现原理?它能保证原子性吗?

答案:

三大特性:
1. 可见性:写操作立即刷新到主内存
2. 有序性:禁止指令重排序
3. 不保证原子性

实现原理:
1. 内存屏障:
   - 写操作前加StoreStore屏障
   - 写操作后加StoreLoad屏障
   - 读操作前加LoadLoad屏障
   - 读操作后加LoadStore屏障
2. 缓存一致性协议(MESI)

原子性示例:
// 以下操作不是原子的
volatile int count = 0;
count++; // 实际是read-modify-write三步操作

适用场景:
1. 状态标志位
2. 双重检查锁定(DCL)
3. 观察者模式中的发布-订阅

三、JUC并发工具包

1. AQS原理

Q6: AbstractQueuedSynchronizer的实现原理?

答案:

核心组成:
1. volatile int state:同步状态
2. Node:双向CLH队列节点
3. ConditionObject:条件变量

工作流程:
1. 获取锁:
   - tryAcquire()尝试获取
   - 失败则加入队列自旋/阻塞
2. 释放锁:
   - tryRelease()释放资源
   - 唤醒后继节点

关键方法:
1. acquire()/release():独占模式
2. acquireShared()/releaseShared():共享模式
3. hasQueuedPredecessors():公平锁判断

应用实例:
ReentrantLock、CountDownLatch、Semaphore等都基于AQS实现

2. ConcurrentHashMap

Q7: ConcurrentHashMap在JDK7和JDK8中的实现有什么区别?

答案:

JDK7实现:
1. 分段锁(Segment继承ReentrantLock)
2. 默认16个段,并发度固定
3. 段内是HashEntry数组+链表

JDK8改进:
1. 取消分段锁,改用CAS+synchronized
2. 数据结构:数组+链表+红黑树
3. 并发控制:
   - 初始化:CAS
   - put操作:
     a. 空桶:CAS插入
     b. 非空:synchronized锁头节点
4. size计算:baseCount+CounterCell数组

性能对比:
JDK8的并发度更高,内存占用更小,查询效率提升(红黑树)

四、线程池

1. 核心参数

Q8: ThreadPoolExecutor的7个核心参数是什么?如何合理配置?

答案:

7大参数:
1. corePoolSize:核心线程数(常驻)
2. maximumPoolSize:最大线程数
3. keepAliveTime:空闲线程存活时间
4. unit:时间单位
5. workQueue:任务队列
6. threadFactory:线程工厂
7. handler:拒绝策略

配置建议:
1. CPU密集型:
   coreSize = CPU核数 + 1
   maxSize = coreSize
2. IO密集型:
   coreSize = CPU核数 × 2
   maxSize = coreSize × (1 + WT/ST)
   (WT:平均等待时间, ST:平均服务时间)
3. 队列选择:
   - 快速响应:SynchronousQueue
   - 无界队列:LinkedBlockingQueue
   - 有界队列:ArrayBlockingQueue

监控指标:
1. 活跃线程数
2. 队列堆积量
3. 最大线程使用量

2. 执行流程

Q9: 描述线程池的任务执行流程?

答案:

执行流程图:
新任务提交 → 
1. 核心线程未满?→ 创建新线程执行
2. 核心线程已满?→ 入队列等待
3. 队列已满?→ 创建非核心线程执行
4. 达到maxSize?→ 执行拒绝策略

关键方法:
1. execute():提交任务入口
2. addWorker():创建新线程
3. getTask():从队列获取任务

拒绝策略:
1. AbortPolicy:抛出RejectedExecutionException
2. CallerRunsPolicy:调用者线程执行
3. DiscardPolicy:静默丢弃
4. DiscardOldestPolicy:丢弃队列最老任务

五、高级并发模式

1. Fork/Join框架

Q10: ForkJoinPool的工作原理?

答案:

设计特点:
1. 工作窃取(Work-Stealing)算法
2. 每个线程维护双端队列
3. 分治任务(ForkJoinTask)

使用步骤:
1. 继承RecursiveTask(有返回值)或RecursiveAction
2. 实现compute()方法
3. 调用fork()分解任务
4. 调用join()合并结果

示例:计算1~n的和
class SumTask extends RecursiveTask<Long> {
    protected Long compute() {
        if (区间足够小) {
            return 直接计算;
        } else {
            SumTask left = new SumTask(前半区间);
            left.fork();
            SumTask right = new SumTask(后半区间);
            return right.compute() + left.join();
        }
    }
}

2. Disruptor框架

Q11: Disruptor为什么比BlockingQueue快?

答案:

高性能原理:
1. 环形数组结构:
   - 预分配内存
   - 无GC压力
2. 消除伪共享:
   - 缓存行填充(Padding)
3. 无锁设计:
   - 序列号控制(Sequence)
   - CAS操作
4. 批量处理:
   - 一次获取多个事件

关键组件:
1. RingBuffer:数据存储
2. Sequence:生产/消费进度
3. EventProcessor:事件处理器
4. WaitStrategy:等待策略

性能对比:
Disruptor吞吐量可达百万级/秒,延迟在微秒级

六、并发问题排查

1. 死锁诊断

Q12: 如何排查和解决Java死锁问题?

答案:

诊断步骤:
1. jstack <pid>:
   - 查找"Found one Java-level deadlock"
   - 查看线程堆栈和锁持有情况
2. JConsole/VisualVM:
   - 线程监控→检测死锁
3. Arthas:
   - thread -b 命令直接定位死锁

预防措施:
1. 锁顺序:
   - 全局定义锁获取顺序
2. 超时机制:
   - tryLock(timeout)
3. 避免嵌套锁:
   - 减少同步代码块嵌套
4. 使用并发容器:
   - 如ConcurrentHashMap

解决案例:
// 错误的锁顺序
Thread1: 获取锁A → 尝试获取锁B
Thread2: 获取锁B → 尝试获取锁A

修正方案:统一按A→B顺序获取锁

2. 线程安全设计

Q13: 如何设计线程安全的单例模式?

答案:

1. 饿汉式(线程安全):
   class Singleton {
       private static final Singleton instance = new Singleton();
       private Singleton() {}
       public static Singleton getInstance() {
           return instance;
       }
   }

2. 双重检查锁(DCL):
   class Singleton {
       private volatile static Singleton instance;
       private Singleton() {}
       public static Singleton getInstance() {
           if (instance == null) {
               synchronized (Singleton.class) {
                   if (instance == null) {
                       instance = new Singleton();
                   }
               }
           }
           return instance;
       }
   }

3. 静态内部类:
   class Singleton {
       private Singleton() {}
       private static class Holder {
           static final Singleton INSTANCE = new Singleton();
       }
       public static Singleton getInstance() {
           return Holder.INSTANCE;
       }
   }

4. 枚举式(最佳实践):
   enum Singleton {
       INSTANCE;
       public void doSomething() {...}
   }

以上内容涵盖了Java多线程的核心知识点,建议结合具体项目经验准备实际案例。对于高级岗位,面试官通常会深入追问实现细节和性能优化方面的实践。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值