🔥 本文深入剖析Java并发编程中的synchronized与ReentrantLock在中断机制上的区别。通过实际案例演示和源码分析,帮你彻底理解:
- synchronized为什么不支持中断?
- ReentrantLock如何实现可中断锁?
- 线程六大状态与中断机制的关系
- 实战中如何选择合适的锁机制
📌 建议收藏,面试必备知识点!
📚博主匠心之作,强推专栏:
Java中的synchronized与中断机制探索
大家好啊!好久未见、甚是想念~
今天想跟大家聊一个挺有意思的话题 —— Java中的响应中断问题。虽然在日常业务开发中可能用得不多,但是了解一下还是很有意思的。让我以一个同为Java开发者的身份,带大家一起探索一下这个机制。
先聊聊锁
说到中断,就不得不先聊聊我们最熟悉的synchronized
。这可是JVM自带的最轻量级、性能最高的可重入锁。它的设计初衷就是让开发者能够屏蔽底层细节,拿来就能用,特别贴心!
后来呢,因为业务需求越来越复杂,就出现了很多基于AQS的工具锁。这些锁虽然在性能上可能比不上synchronized
,但是提供了更多的功能特性。这也印证了那句老话:“有得必有失”,“万事皆为权衡”。
来点有意思的
给大家出个小题:
假设我们有这么一个场景 —— 在Java程序里定义了一个线程A,这个线程要执行的代码被synchronized
锁保护着。如果此时在主线程中调用这个线程的中断方法,你觉得线程A会中断并抛出中断异常吗?
那如果换成ReentrantLock
呢?结果会一样吗?
来看看具体的代码示例:
// 使用synchronized的情况
public class SynchronizedExample {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(() -> {
try {
synchronized (lock) {
// 模拟长时间运行
Thread.sleep(10000);
}
} catch (InterruptedException e) {
System.out.println("synchronized中的线程被中断了!"); // 这句话不会打印
}
});
threadA.start();
Thread.sleep(100); // 确保线程A已经获得了锁
threadA.interrupt(); // 尝试中断线程
System.out.println("尝试中断synchronized中的线程");
}
}
// 使用ReentrantLock的情况
public class ReentrantLockExample {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(() -> {
try {
lock.lockInterruptibly(); // 可中断的加锁
try {
// 模拟长时间运行
Thread.sleep(10000);
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("ReentrantLock中的线程被中断了!"); // 这句话会打印
}
});
threadA.start();
Thread.sleep(100); // 确保线程A已经获得了锁
threadA.interrupt(); // 尝试中断线程
System.out.println("尝试中断ReentrantLock中的线程");
}
}
哈哈,看到这两个问题,相信大家也猜到了 —— 肯定一个会中断,一个不会。实际情况是:
- 使用
synchronized
的情况下不会中断 - 使用
ReentrantLock
会中断
为什么会这样?
这就要说到Java中线程的六种状态了:
-
新建(NEW)
- 线程被创建,但还没有调用start()方法
-
可运行(RUNNABLE)
- 包含了操作系统中的"运行中"和"就绪"两种状态
- 线程可能正在运行,也可能正在等待CPU时间片
-
阻塞(BLOCKED)
- 线程正在等待获取监视器锁(monitor lock)
- 这种状态特指在synchronized同步块或方法时等待锁的情况
- 这种状态下的线程是不响应中断的
-
等待(WAITING)
- 线程进入无限期等待状态
- 调用如下方法会进入这种状态:
- Object.wait()
- Thread.join()
- LockSupport.park()
- 需要其他线程显式唤醒
-
超时等待(TIMED_WAITING)
- 线程进入有限期等待状态
- 调用如下方法会进入这种状态:
- Thread.sleep(timeout)
- Object.wait(timeout)
- Thread.join(timeout)
- LockSupport.parkNanos(timeout)
- LockSupport.parkUntil(timeout)
- 在指定时间后会自动唤醒
-
终止(TERMINATED)
- 线程执行完毕或因异常退出了run()方法
- 线程结束后的最终状态
使用synchronized
时,线程会进入BLOCKED(阻塞)状态,这是一种不响应中断的状态。即使你调用了中断方法,线程也不会被唤醒。
而ReentrantLock
是基于AQS实现的,等待锁的线程会进入一个阻塞队列,此时线程处于可响应中断的状态(注意,不是BLOCKED状态)。当我们调用线程的interrupt()
方法时,应用程序会调用操作系统的中断接口,触发线程苏醒。操作系统会返回一个标志,告诉应用程序线程已经苏醒,然后应用程序就会抛出中断异常,这样就能被我们的业务代码捕获到啦!
再往下就是进行业务的相关处理。
为什么synchronized最初要这么设计?
这其实是有原因的:
-
设计理念简单明确:
- 简单易用
- 性能优先
- 不需要开发者操心太多细节
-
性能至上:
- 是JVM原生支持的,经过了大量优化(偏向锁、轻量级锁、自旋锁)
- 如果加入中断处理,势必会带来额外开销
- 而且大多数场景下,我们确实也用不到中断锁等待这个功能
-
后来为什么要有ReentrantLock?
随着并发编程的发展,开发者发现synchronized
有些局限性:- 不支持尝试加锁(try-lock)
- 不支持超时机制
- 不支持中断
- 不支持读写分离
所以在JDK 5中,Java引入了java.util.concurrent.locks
包,其中最重要的就是ReentrantLock
,专门用来解决这些问题。
怎么样,现在对Java中的中断机制理解得更清楚了吗?😊
写在最后
🎉 通过本文的学习,相信大家对Java中的synchronized与中断机制有了更深入的理解。特别是理解了为什么synchronized和ReentrantLock在处理中断时会有不同的表现,这对我们在实际开发中选择合适的锁机制很有帮助!
📚博主匠心之作,强推专栏:
💌 与博主互动 & 技术支持
👋 读到这里,不妨留下你的想法和问题,博主会第一时间回复!
❓ 遇到技术难题?
- 在项目中遇到线程中断相关的问题?
- 需要并发编程的最佳实践建议?
- 作业&实验需要帮助?
🤝 专业服务
① 🛠️ 技术咨询与问题解答:Java/Python/前后端/数据库/算法等各领域问题
② 🚀 项目开发与合作:
- 前端开发:Web应用、响应式设计、交互体验优化
- 后端开发:Java/Python/Node.js微服务、API设计
- 移动应用:H5、小程序、公众号开发
- 全栈解决方案:从需求分析到部署维护
③ 📝 代码审查与优化:性能调优、架构改进、最佳实践建议
④ 📚 学习指导与作业辅导:为在校学生提供编程作业指导和学习路径规划
🔍 如有合作需求,欢迎私信联系,期待与您共创价值!
🎯 我是果冻~,一个热爱技术、乐于分享的开发者
📚 更多精彩内容,欢迎关注我的博客,与我交流!
🌟 如果本文对你有帮助,别忘了点赞👍收藏⭐评论💬一键三连支持博主!
期待与你在评论区相遇,共同进步!