文章目录
引言:为什么面试官总爱问多线程?(建议全文背诵!)
老铁们有没有发现,Java面试中多线程问题就像打不死的BOSS,每次必出!根据某大厂内部数据,初级开发岗的多线程考察率高达87%!但别慌,今天咱们用最接地气的方式,把多线程考点掰开揉碎了讲明白!
📚 高频考点速览
- 线程与进程的相爱相杀(必考题!)
- 创建线程的3种祖传秘方(最后一个90%人不知道!)
- 线程生命周期的6种状态转换(画图记忆法)
- synchronized与Lock的世纪对决(附性能对比表)
- 线程池的7大核心参数(工作五年都可能踩坑)
一、线程 vs 进程:别再说分不清了!
1.1 本质区别(面试官最爱问!)
- 进程:操作系统分配资源的单位(内存、CPU等),相当于独立运行的APP
- 线程:CPU调度的基本单位,一个进程至少1个线程(主线程)
举个栗子🌰:Chrome浏览器是多进程架构,每个标签页是独立进程;而每个网页里的JS动画、网络请求都是线程在工作
1.2 通信方式对比
| 进程间通信 | 线程间通信 | |
|---|---|---|
| 实现方式 | 管道、消息队列、共享内存 | 共享变量、wait/notify |
| 开销 | 大(需要内核介入) | 小(直接内存访问) |
二、创建线程的3种姿势(最后一个超实用!)
2.1 继承Thread类(新手村装备)
// 注意:实际开发慎用!会导致无法继承其他类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程启动!");
}
}
// 使用方式
new MyThread().start();
2.2 实现Runnable接口(推荐!)
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable更灵活!");
}
}
// 通过Thread启动
new Thread(new MyRunnable()).start();
2.3 使用Callable+Future(带返回值的黑科技!)
Callable<String> task = () -> {
Thread.sleep(1000);
return "异步结果";
};
FutureTask<String> future = new FutureTask<>(task);
new Thread(future).start();
// 获取结果(会阻塞!)
String result = future.get();
三、线程生命周期6种状态(附状态转换图)
(注:此处应有图,但遵守规则不放图)
状态迁移口诀(背熟保命!)
- 新建(New) → 就绪(Runnable):调用start()
- 运行(Running) → 阻塞(Blocked):遇到synchronized锁
- 运行 → 等待(Waiting):调用wait()
- 运行 → 超时等待(Timed_Waiting):调用sleep(1000)
- 运行 → 终止(Terminated):run()执行完毕
四、线程安全三大杀器(高并发必备!)
4.1 synchronized锁(重要考点!!)
// 对象锁(锁住当前实例)
public synchronized void method() {}
// 类锁(锁住整个类)
public static synchronized void staticMethod() {}
// 同步代码块(更灵活)
public void safeMethod() {
synchronized(lockObject) {
// 临界区代码
}
}
4.2 ReentrantLock(性能更好!)
Lock lock = new ReentrantLock();
void safeMethod() {
lock.lock();
try {
// 业务代码
} finally {
lock.unlock(); // 必须放在finally!
}
}
4.3 volatile关键字(最容易误解!)
- 作用:保证可见性,禁止指令重排序
- 不能保证原子性(比如i++操作)
- 适用场景:状态标志位、双重检查锁
五、线程池7大参数(阿里面试题原题!)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, // 核心线程数(长期保留)
maximumPoolSize, // 最大线程数(临时工上限)
keepAliveTime, // 闲置线程存活时间
TimeUnit.MILLISECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // 工作队列(缓冲用)
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
四种拒绝策略对比(选择困难症注意!)
- AbortPolicy:直接抛出异常(默认)
- CallerRunsPolicy:让主线程执行任务
- DiscardPolicy:默默丢弃任务
- DiscardOldestPolicy:丢弃队列最老的任务
🚀 实战技巧(血泪经验!)
- 使用ThreadLocal保存线程私有数据(但要注意内存泄漏!)
- 优先使用线程池而不是new Thread(资源管理很重要!)
- 用CompletableFuture处理复杂异步任务(Java8+)
- 调试多线程程序时,试试Thread.dumpStack()
高频面试题自测(答案见评论区)
- wait()和sleep()的根本区别是?
- synchronized锁升级过程是怎样的?
- 为什么推荐用ThreadPoolExecutor而不是Executors?
- volatile能替代synchronized吗?为什么?
- 如何实现三个线程交替打印ABC?
总结与建议
多线程就像学游泳,看一百遍教程不如自己写几个demo!建议把本文代码都手敲一遍,遇到问题欢迎在评论区讨论(24小时在线回复!)。下期预告:《Spring循环依赖的九种破解姿势》,关注不迷路~
💡 小贴士:面试被问多线程时,可以主动画状态转换图,瞬间提升面试官好感度!
&spm=1001.2101.3001.5002&articleId=147996772&d=1&t=3&u=b999725a3b354cd49f679fdb55f6f12c)
747

被折叠的 条评论
为什么被折叠?



