用阻塞队列实现一个生产者消费者模型?synchronized和lock有什么区别?
多线程当中的阻塞队列
主要实现类有
- ArrayBlockingQueue是一个基于数组结构的有界阻塞队列,此队列按FIFO原则对元素进行排序
- LinkedBlockingQueue是一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue
- SynchronousQueue是一个不存储元素的阻塞队列,单个插入操作必须等到另一个线程调用移除操作,否则插 入操作一直处于阻塞状态
1. 阻塞队列概念
阻塞队列通俗来说,是一个队列,而一个阻塞队列再数据结构中所起的作用大致如下图
线程1往阻塞队列中添加元素,而线程2从阻塞队列中移除元素
当阻塞队列是空时,从队列中获取元素的操作会被阻塞
当阻塞队列是满时,从队列中添加元素的操作会被阻塞
- 试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程向空的队列插入新的元素。
- 试图向已满的阻塞队列中添加新元素的线程同样会被阻塞,直到其他的线程从列中移除一个或者多个元素或者完全清空队列后使队列重新变得空闲起来并后续新增
很像生产者消费者模型!
2. 为什么要用阻塞队列?好处是什么?
-
在多线程领域:所谓阻塞,在某些情况下会挂起线程,一旦满足条件,被挂起的线程又会自动被唤醒
-
为什么需要BlockingQueue?
答:好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一 手包办了
-
在concurrent包发布以前,在多线程环境下,我们每个程序员都必须自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们程序带来不小的复杂度
-
阻塞队列图示:
3. BlockingQueue的核心方法
方法类型 | 抛出异常 | 特殊值 | 阻塞 | 超时 |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除 | remove() | poll() | take | poll(time,unit) |
检查 | element() | peek() | 不可用 | 不可用 |
可以看到,对于不同的方法类型,内部对应插入移除以及检查方法对应的api都不同,所以,想要对不同的对列操作时候,需要考虑是否需要抛出异常?线程是否需要阻塞等,这样对应不同的方法才能达到事半功倍的效果
参照源码
对上述方法类型做描述:
方法类型 | 描述 |
---|---|
抛出异常 | 当阻塞队列满时,再往队列中add会抛IllegalStateException: Queue full 当阻塞队列空时,在网队列里remove会抛 NoSuchElementException (这两个都是异常) |
特殊值 | 插入方法,成功true失败false 移除方法,成功返回出队列的元素,队列里没有就返回null |
阻塞 | 当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞线程直到put数据或响应中断退出 当阻塞队列空时,消费者线程试图从队列take元素,队列会一直阻塞消费者线程直到队列可用 |
超时退出 | 当阻塞队列满时,队列会阻塞生产者线程一定时间 超过限时后生产者线程会退出 |
4. 种类分析
4.1 接口查看
我们知道,阻塞队列是BlockingQueue,我们打开Diagram图查看类之间的关系可得
Queue是继承了Queue的接口,同时Queue接口又继承了Collection接口,那么BlockingQueue作为接口,那么一定会有实现类,不同实现类使用不同数据结构实现,完成的操作也不相同,我们这里列出BlockingQueue的7个实现类,分别为:
- ArrayBlockingQueue:由数组结构组成的有界阻塞队列
- LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为 Integer.MAX_VALUE )阻塞队列
- PriorityBlockingQueue:支持优先级排序的无界阻塞队列
- DelayQueue:使用优先级队列实现的延迟无界阻塞队列
- SychronousQueue:不存储元素的阻塞队列,也即单个元素的队列
- LinkedTransferQueue:由链表结构组成的无界阻塞队列
- LinkedBlockingDeque:由双向链表结构组成的双向阻塞队列。
我们常用的实现类,在上面已经用重点符号表示了,ArrayBlockingQueue类似我们常用的ArrayList,底层数据结构是数组组成的队列,LinkedBlockingQueue就类似我们常用的LinkedList,底层数据结构是链表组成的队列,这里要注意LinkedBlockingDeque,那么Deque的接口继承关系如下所示:
对于我们常用的实现类,我们可以发现SychronousQueue我们不常用也不了解,什么是单个元素的队列?那么我们下面用代码实现一下:
4.2 SychronousQueue
概念:SynchronousQueue没有容量,与其他BlockingQueue不同,SychronousQueue是一个不存储元素的BlockingQueue,每一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然
代码实例:
代码结果为:
5. 用在什么地方?
讲了这么多BlockingQueue的优点,那么阻塞队列一般用在哪里?
- 生产者消费者模式
5.1 传统版生产者消费者模式
运行结果为:
可以看到对共享变量进行增加或者减少操作的时候需要进行通知,同时对内部变量进行volatile保证变量的可见性以及禁止指令重排,可以更好地对生产者消费者模型当中操作的保证
5.2 阻塞队列版生产者消费者模式
结果:
可以看到上述并没有用到Lock以及synchronized,而仅仅用到了阻塞队列以及原子整型类,就可以实现生产者消费者模型,也就是不用程序员关心具体的加锁解锁过程,而是关心具体的业务逻辑
6. synchronized和lock有什么区别?用新的lock有什么好处?请举例
区别:
-
原始构成
-
synchronized是关键字属于jvm
其中jvm会将其字节码运行为monitorenter以及monitorexit
monitorenter,底层是通过monitor对象来完成,其实wait/notify等方法也依赖于monitor对象只有在同 步或方法中才能掉wait/notify等方法
monitorexit
-
Lock是具体类,是api层面的锁(java.util.concurrent.locks.Lock)
-
-
使用方法
- sychronized不需要用户取手动释放锁,当synchronized代码执行完后系统会自动让线程释放对锁的占用
- ReentrantLock则需要用户去手动释放锁若没有主动释放锁,就有可能导致出现死锁现象,需要lock()和 unlock()方法配合try/finally语句块来完成
-
等待是否可中断
- synchronized不可中断,除非抛出异常或者正常运行完成
- ReentrantLock可中断,设置超时方法
tryLock(long timeout, TimeUnit unit)
,或者lockInterruptibly()
放代码块中,调用interrupt()方法可中断
-
加锁是否公平
- synchronized是非公平锁
- ReentrantLock两者都可以,默认也是非公平锁,构造方法可以传入boolean值,true为公平锁,false为非公平锁
-
锁绑定多个条件Condition
- synchronized没有
- ReentrantLock用来实现分组唤醒需要要唤醒的线程们,可以精确唤醒,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程
代码:
我们可以清楚地看到Lock可以创建多个Condition,同时对不同的Condition调用await以及signal方法,可以对不同的线程进行操作,这就是Lock比synchronized更方便的原因,synchronized只能对所有线程进行notifyall()
方法,随机唤醒线程
注意:notifyall()
方法是Object类当中的方法!
__EOF__

本文链接:https://www.cnblogs.com/yuxueyyz/p/14994915.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!