上一篇
java的线程知识怎么回答
- 后端开发
- 2025-08-06
- 5
Java线程通过继承Thread类或实现Runnable接口创建,可控制并发执行,需关注线程安全,使用synchronized/Lock等机制避免竞态条件,合理管理生命周期及资源共享
核心概念解析
什么是线程?
线程是操作系统调度的最小单位,代表程序执行流,一个进程可包含多个线程,共享进程资源(如内存空间),但拥有独立的栈空间、程序计数器和局部变量,Java通过java.lang.Thread
类及接口提供对线程的支持。
特性 | 说明 |
---|---|
轻量级 | 线程创建和管理成本低,适合高并发场景 |
共享性 | 同一进程内的线程共享堆和方法区,不共享栈 |
并发执行 | 多线程可并行利用CPU核心,提升性能 |
异步性 | 主线程无需等待子线程完成即可继续执行 |
线程 vs 进程
维度 | 进程 | 线程 |
---|---|---|
资源占用 | 独立分配内存/文件句柄 | 共享进程资源,仅保留必要数据 |
通信成本 | 进程间通信(IPC)复杂 | 线程间通信简单(共享变量) |
切换开销 | 大(涉及上下文切换) | 小(仅需保存少量寄存器) |
适用场景 | 隔离任务(如浏览器标签页) | 协作任务(如日志记录+业务处理) |
线程创建方式
方式1:继承Thread类
class MyThread extends Thread { @Override public void run() { System.out.println("线程执行"); } } // 启动线程 new MyThread().start(); // ️ 勿直接调用run()!
特点:简单直观,但破坏了面向对象原则(Java单继承限制)。
方式2:实现Runnable接口
class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程执行"); } } // 启动线程 new Thread(new MyRunnable()).start();
优势:解耦任务与线程管理,支持多线程复用同一任务实例。
方式3:Callable + FutureTask(带返回值)
import java.util.concurrent.; class MyCallable implements Callable<String> { @Override public String call() throws Exception { return "计算结果"; } } // 提交任务并获取未来结果 FutureTask<String> task = new FutureTask<>(new MyCallable()); new Thread(task).start(); String result = task.get(); // 阻塞直到结果可用
适用场景:需要返回值或抛出异常的任务。
线程生命周期与状态转换
六种状态及转换
状态 | 描述 | 触发条件 |
---|---|---|
NEW | 新建未启动 | new Thread() |
RUNNABLE | 正在JVM中执行或等待CPU时间片 | start() / yield() 后重新调度 |
BLOCKED | 等待监视器锁/IO操作 | synchronized 块/wait() |
WAITING | 等待其他线程唤醒(无限期) | Object.wait() /Condition.await() |
TIMED_WAITING | 限时等待(超时自动唤醒) | sleep(time) /join(timeout) |
TERMINATED | 执行完毕或被终止 | run() 结束/interrupt() |
关键方法行为
方法 | 作用 | 注意事项 |
---|---|---|
start() |
启动新线程,JVM调用run() |
必须调用,否则不会执行任务 |
run() |
线程主体逻辑 | 直接调用等同于普通方法调用 |
sleep(ms) |
当前线程休眠指定毫秒 | ⏰ 不释放锁,到期自动唤醒 |
yield() |
提示调度器让出CPU给同级线程 | 低优先级不代表立即切换 |
join() |
等待当前线程终止 | ⏳ 可用于协调多线程执行顺序 |
interrupt() |
中断线程(设置中断标志) | 需配合isInterrupted() 检查 |
线程同步机制
为什么需要同步?
多线程访问共享资源可能导致数据不一致(竞态条件),两个线程同时修改账户余额。
同步解决方案
方案 | 语法示例 | 特点 |
---|---|---|
synchronized |
synchronized(this) { ... } |
内置锁,隐式获取/释放锁 |
ReentrantLock |
Lock lock = new ReentrantLock(); lock.lock(); ... lock.unlock(); |
可中断、超时尝试获取锁 |
volatile |
private volatile boolean flag; |
️ 确保可见性,禁止指令重排序 |
AtomicXXX |
AtomicInteger count = new AtomicInteger(0); count.getAndIncrement(); |
🧮 原子操作,无锁高性能 |
死锁与防范
死锁四要素:互斥、占有并等待、不可剥夺、循环等待。
预防措施:
- 按固定顺序加锁(全局排序)
- 使用
tryLock(timeout, unit)
避免无限等待 - 通过银行家算法检测安全性
线程池管理
️ 为何使用线程池?
- 减少频繁创建销毁线程的开销
- 控制最大并发数,防止资源耗尽
- 统一管理线程生命周期
Executor框架核心组件
类/接口 | 功能 | 典型用法 |
---|---|---|
Executors |
工厂类,创建预定义线程池 | Executors.newFixedThreadPool(5) |
ThreadPoolExecutor |
可配置参数的核心线程池 | 核心池大小、队列容量、拒绝策略 |
ScheduledExecutorService |
定时/周期性任务执行 | scheduleAtFixedRate() |
ForkJoinPool |
分治任务并行处理 | 递归拆分任务,工作窃取算法 |
️ 常用线程池类型对比
类型 | 核心线程数 | 最大线程数 | 任务队列 | 适用场景 |
---|---|---|---|---|
newFixedThreadPool |
固定值 | 固定值 | LinkedBlockingQueue | 长期稳定负载 |
newCachedThreadPool |
0 | Integer.MAX | SynchronousQueue | 突发短任务(快速扩容收缩) |
newSingleThreadExecutor |
1 | 1 | LinkedBlockingQueue | 保证顺序执行 |
newScheduledThreadPool |
固定值 | 固定值 | DelayedWorkQueue | 定时/延期任务 |
高级主题
守护线程(Daemon Thread)
- 定义:后台服务线程,当所有非守护线程结束时自动终止。
- 设置方式:
thread.setDaemon(true);
(需在start()
前调用) - 典型应用:垃圾回收、心跳检测、监控日志。
线程优先级
- 范围:1(最低)~ 10(最高),默认为5。
- 注意:仅影响调度概率,不保证执行顺序!实际效果依赖操作系统策略。
八锁模型与CAS
- 八锁模型:
notify()
/notifyAll()
唤醒随机/全部等待线程。 - CAS(Compare-And-Swap):硬件原子操作,用于实现无锁数据结构(如
ConcurrentHashMap
)。
常见问题与最佳实践
Q1: 为什么不应该直接调用run()
方法?
答:run()
只是普通方法调用,会在当前线程执行,无法发挥多线程优势,必须调用start()
让JVM创建新线程并调用run()
。
Q2: 如何在多线程间安全传递数据?
答:推荐方案:
- 生产者-消费者模式:使用
BlockingQueue
(如LinkedBlockingQueue
)。 - 不变性设计:传递不可变对象(如
final
修饰的集合)。 - 拷贝防御:在同步块内复制必要数据到局部变量。
- 线程本地存储:使用
ThreadLocal
保存线程私有数据。
相关问答FAQs
Q: Java中如何停止一个正在运行的线程?
A: 推荐两种方式:
- 协作式中断:调用
thread.interrupt()
,线程内部定期检查Thread.currentThread().isInterrupted()
并退出。 - 暴力终止:使用
Thread.stop()
(已过时,不安全,可能导致数据不一致)。
️ 注意:强制终止可能造成资源泄漏,应优先采用协作式中断。
Q: 什么是伪共享(False Sharing)?如何避免?
A: 伪共享发生在多线程频繁修改相邻缓存行的变量,导致缓存行反复失效,解决方法:
- 填充数组:在字段间添加无关变量增大间距。
- 使用
@sun.misc.Contended
注解(JDK8+):强制变量独占缓存行。 - 分离高频变量:将热点