Java Spring应用死锁、多线程基础及Java队列实现技术咨询
嘿,我帮你拆解这两个问题,都是多线程入门绕不开的核心点,慢慢来~
一、死锁核心概念与学习指引
先把死锁相关的关键术语理清楚,再给你明确的学习方向:
关键术语
- 死锁:两个或多个线程互相攥着对方必须的资源,谁都不肯先放手,结果所有线程都卡在那儿动不了的状态。
- 互斥条件:某个资源同一时间只能被一个线程占用(比如Java里的
synchronized锁就是独占的)。 - 请求与保持条件:线程已经拿着一些资源了,还在请求别的资源,而且拿在手里的资源不肯放。
- 不可剥夺条件:线程拿到的资源不能被硬抢走,只能自己主动释放。
- 循环等待条件:线程之间形成了一个环形的资源依赖链——比如线程A等线程B的资源,线程B等线程C的,线程C又等线程A的。
学习方向
- 先啃透死锁的四个必要条件——这是死锁的本质,记住:只有四个条件同时满足,死锁才会发生,少一个都不行。
- 学死锁排查:Java里可以用
jstack命令导出线程栈,找那些BLOCKED状态的线程,看它们在等什么资源,很容易定位死锁。 - 掌握死锁的预防与避免:
- 预防:直接打破四个条件中的一个,比如给资源排个固定顺序,所有线程都按这个顺序拿资源(打破循环等待);或者用可中断的锁(打破不可剥夺)。
- 避免:比如银行家算法,动态判断分配资源会不会导致死锁,不过实际开发里用得不多,了解思路就行。
- 动手写案例:自己写个简单的死锁代码(比如两个线程互相抢两把锁),然后尝试修复它,比光看理论管用多了。
二、Java中实现队列暂存待处理元素(以通知场景为例)
Java本身就有成熟的队列实现,尤其是针对多线程场景的线程安全队列,不用自己手动写同步逻辑,省心又不容易出bug。
常用线程安全队列选型
LinkedBlockingQueue:基于链表的有界/无界队列,适合生产者-消费者模式,性能稳定。如果是大量通知,建议设置一个合理的容量(比如1000),避免队列无限膨胀占满内存。ArrayBlockingQueue:基于数组的有界队列,比链表队列更省内存,适合固定容量的场景。ConcurrentLinkedQueue:无界的非阻塞队列,用CAS操作实现线程安全,高并发下性能更好,但要注意内存溢出问题(生成速度远大于处理速度时会炸)。
示例代码:生产者-消费者处理通知
比如你有生成通知的线程(生产者)和处理通知的线程(消费者),用LinkedBlockingQueue来暂存:
import java.util.concurrent.LinkedBlockingQueue; // 通知实体类,存你需要的内容 class Notification { private String content; private long createTime; public Notification(String content) { this.content = content; this.createTime = System.currentTimeMillis(); } public String getContent() { return content; } } // 队列管理器,封装队列的操作 class NotificationQueue { // 设置队列容量为1000,防止内存溢出 private final LinkedBlockingQueue<Notification> queue = new LinkedBlockingQueue<>(1000); // 添加通知到队列,队列满了会阻塞直到有空间 public void addNotification(Notification notification) throws InterruptedException { queue.put(notification); } // 取出通知处理,队列空了会阻塞直到有新通知 public Notification takeNotification() throws InterruptedException { return queue.take(); } } // 测试逻辑 public class NotificationHandler { public static void main(String[] args) { NotificationQueue queue = new NotificationQueue(); // 启动3个消费者线程处理通知 for (int i = 0; i < 3; i++) { new Thread(() -> { try { while (!Thread.currentThread().isInterrupted()) { Notification notice = queue.takeNotification(); System.out.println("处理通知:" + notice.getContent() + " | 线程:" + Thread.currentThread().getName()); // 模拟处理耗时 Thread.sleep(150); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println(Thread.currentThread().getName() + "被中断"); } }, "消费者-" + i).start(); } // 生产者线程生成大量通知 new Thread(() -> { try { int count = 0; while (!Thread.currentThread().isInterrupted()) { Notification newNotice = new Notification("用户消息-" + count++); queue.addNotification(newNotice); System.out.println("添加通知:" + newNotice.getContent() + " | 队列当前大小:" + queue.queue.size()); // 模拟通知生成速度 Thread.sleep(80); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println("生产者线程被中断"); } }, "通知生成器").start(); } }
实用小贴士
- 队列满了怎么办?如果不想阻塞,可以用
offer()方法,它会在队列满时返回false,你可以做降级处理(比如记录日志、丢弃旧通知)。 - 如果需要按优先级处理通知,可以用
PriorityBlockingQueue,自定义比较器来排序元素。 - 非阻塞场景用
ConcurrentLinkedQueue,它的poll()方法会立即返回null(队列空时),适合不需要等待的逻辑。
内容的提问来源于stack exchange,提问作者NoName123
相关产品推荐
相关产品推荐

