今天不学习,明天变垃圾
本文的主要内容:多线程案例中的【阻塞队列 + 生产者消费者模型】。
- 【常用,所以工作中经常把消息队列这样的数据结构单独实现成一个小程序,并且部署在一组服务器上,称为“消息队列服务器”,也就是平时常说的“MQ”,其实也就是一种常用的“中间件”
- “中间件”其实就是一类“通用”服务器的统称,与业务无关,如MySQL】
阻塞队列是一个特殊的队列,但是其确实是 “先进先出” 的。
阻塞队列特点:
① 线程安全
② 带有阻塞功能:
A)如果队列满了还继续入队列,此时入队操作就会阻塞;直到队列不满,入队列才能成功
B)如果队列空了还继续出队列,此时出队操作就会阻塞;直到队列不空,出队列才能成功
阻塞队列的典型应用场景:生产者-消费者模型(描述的是多线程协同工作的一种方式),该模型能够较好地解决锁冲突问题。 (举例:包饺子)
耦合:两个模块之间的关联关系,关系越紧密则耦合性越高。
【如:两个服务器直接通信则关联性强,互相影响;
但是如果在两个服务器之间加上一个阻塞队列就有效降低了两个服务器的关联性,耦合性降低,并且这样的话增加/删除服务器也较为方便】
- 也就是说: 生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,此时的阻塞队列就类似于“缓冲区”
② 削峰填谷:(举例:三峡水库)
如果按照没有 生产者消费者模型的写法,外面流量过来的压力就会直接压在每个服务器上,如果某个服务器抗压能力不太行就容易挂。
(队列有三个基本操作:入队列、出队列、取 队首元素)
阻塞队列提供给了带有阻塞的入队列和出队列方法,但是没有提供带有阻塞的取队首元素方法。
标准库的阻塞队列
Demo1标准库
1)BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性。而put、take方法带有阻塞。
2) BlockingQueue是接口。
自己实现一个阻塞队列
1) 先实现一个普通队列
(队列的实现有两个版本:基于链表、基于数组):这里写基于数组的版本(循环队列)
① 循环队列:下标head/tail 如果head == tail就是null/满,如果存入数据则tail++,并且队列中有效元素范围是[head,tail); 而如果到达末尾就又回到开头,达到循环
② 如何区分该队列是空还是满呢?空和满都是head==tail,即指针重合,区分方法就是:要么加一个记录个数的变量,要么浪费一个空间
(具体参考博客:队列实现(循环队列))
2)加上线程安全 (线程安全就是加锁synchronized)
3)加上阻塞实现(队列为空则出队列阻塞,队列满则入队列阻塞)
这里注意稳妥的写法,wait不一定是另一个线程中的notify来唤醒的,也可能是interrupt来唤醒的,如果是interrupt唤醒可能条件就还不成熟,所以需要循环再判断。
(wait被唤醒之后也是要去竞争锁的)
Demo2 生产者消费者模型+自己实现阻塞队列
(尽可能看注释+每次提交)