文章目录

  • ​​阻塞队列理论​​
  • ​​为什么用阻塞队列?有什么好处?​​
  • ​​接口结构和实现类​​
  • ​​阻塞队列的核心方法​​
  • ​​抛出异常组:add/remove​​
  • ​​返回bool值组:offer/poll​​
  • ​​阻塞:put/take​​
  • ​​超时控制:offer/poll​​
  • ​​同步synchronousQueue队列​​
  • ​​线程通信之生产者消费者传统版​​
  • ​​虚假唤醒​​
  • ​​sync与lock的区别​​
  • ​​原始构成​​
  • ​​使用方法​​
  • ​​等待是否可以中断​​
  • ​​加锁方式​​
  • ​​锁要绑定多个条件condition​​

阻塞队列理论

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常


10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_02


10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_03


10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_04


空了,消费者阻塞,满了,生产者阻塞。

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_05


MQ的核心底层原理就是这个。

为什么用阻塞队列?有什么好处?

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_06


有了它,程序员不需要再去控制wait和notify。这是它的厉害之处。

接口结构和实现类

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_07


Collection集合接口有2个子接口,List和阻塞队列BlockingQueue。

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_08


collection-queue接口-阻塞队列接口。

阻塞队列接口有7个实现类。

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_09


链表结构的有界阻塞队列绝对不允许用size默认值(21亿)。可以当作无界了。

10.互联网大厂高频面试题-阻塞队列(上)_多线程_10


标红的三个就是线程池底层用的三个。

其中synchronousQueue:理解为订单不下,我不开工!

queue接口跟list一样,arraylist,linkedlist对应的arrayblockingqueue,linkedbolockingqueue。

阻塞队列的核心方法

10.互联网大厂高频面试题-阻塞队列(上)_多线程_11

抛出异常组:add/remove

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_12


add方法报错演示:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_13


add方法异常触发代码:

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_14


取出元素抛出异常代码演示:

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_15


错误信息展示:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_16


element方法:查看队列排头(先进先出)的一个元素,并不取出,如果没有元素也会抛出异常。

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_17

返回bool值组:offer/poll

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_18


这组api不会抛出异常。是返回的指定的值。

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_19


api测试代码:

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_20


api测试结果:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_21

阻塞:put/take

这一组慎用,但是用好了对程序帮助很大。

测试代码:

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_22

运行结果:注意此时jvm没有停止,在一直尝试添加。消息积压之后,只能堵着。

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_23


10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_24

插入方法和取出方法一旦执行的不顺利(队列满了加不进去,队列空了取不出来)则会堵塞在这。

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_25

超时控制:offer/poll

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_26


10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_27


结果是只阻塞2s,然后返回false。

10.互联网大厂高频面试题-阻塞队列(上)_多线程_28


10.互联网大厂高频面试题-阻塞队列(上)_多线程_29

同步synchronousQueue队列

10.互联网大厂高频面试题-阻塞队列(上)_多线程_30


典型的先下单,再生产的情形。

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_31


空构造是一个非公平锁。并不存储。

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_32


代码演示:

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_33


10.互联网大厂高频面试题-阻塞队列(上)_多线程_34


运行结果:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_35


一定是先等消费者来了,才往里面放,否则就卡在那里。

线程通信之生产者消费者传统版

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_36


阻塞队列用在哪里呢?请看上图。

线程通信模型的迭代:从左到右,是1.0到2.0.

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_37


多线程口诀:高内聚,内耦合,线程操作资源类。判断-干活-唤醒通知。严防多线程状态下的虚假唤醒(多线程判断用while)。

10.互联网大厂高频面试题-阻塞队列(上)_多线程_38


所有的java操作,先是对象,再是对象里的变量。

代码演示:

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_39


10.互联网大厂高频面试题-阻塞队列(上)_多线程_40


10.互联网大厂高频面试题-阻塞队列(上)_多线程_41


线程调用资源类的方法,而不是线程里再去写方法,是资源类自带的对外访问的接口。

测试代码:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_42


10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_43


运行结果:生产一个,消费一个。

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_44


这就是传统版本的生产者消费者模式,就是上面那两个三角形的变动。

虚假唤醒

对于object类的wait和notify方法,api提示了可能发生虚假唤醒。所以方法要放在循环里面。

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_45


所以多线程的判断,你不能用if,要用while。也就是说被唤醒之后,要重新做一次判断,是否真的该轮到我执行了。

想复现虚假唤醒,可以把测试线程翻倍,然后看测试结果。如果不是while判断的话,改为if,就会出现虚假唤醒了。测试结果如下图:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_46


只要是多线程框架的底层,基本都是while判断。比如netty。

两个线程,互相唤醒,没问题,如果线程多了,唤醒了同为生产者/消费者的线程,抢到了锁,就会出问题,也就是所谓的虚假唤醒了。

sync与lock的区别

某学堂-JUC-2019版本讲过。

10.互联网大厂高频面试题-阻塞队列(上)_多线程_47


之前的笔记:

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_48

原始构成

看一下jvm的字节码,sync关键字是依靠monitor系列的关键字实现的。

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_49


sync底层会有2次退出,第一次保证正常退出,第二次保证异常退出。

10.互联网大厂高频面试题-阻塞队列(上)_多线程_50


sync保证你不会产生死锁和底层的一些故障。因为有这两个退出。

而关于new ReentrantLock,底层代码则为:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_51


是API级别的锁。

使用方法

sync不需要手动释放锁,但是Reentrant需要手写lock和unlock。

10.互联网大厂高频面试题-阻塞队列(上)_多线程_52

等待是否可以中断

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_53

加锁方式

10.互联网大厂高频面试题-阻塞队列(上)_多线程_54


10.互联网大厂高频面试题-阻塞队列(上)_多线程_55

锁要绑定多个条件condition

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_56


题目要求:

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_57


代码设计:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_58


10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_59


10.互联网大厂高频面试题-阻塞队列(上)_多线程_60


修改标志位,然后通知:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_61


print15:

10.互联网大厂高频面试题-阻塞队列(上)_抛出异常_62


最后资源类效果:

10.互联网大厂高频面试题-阻塞队列(上)_多线程_63


测试代码:

10.互联网大厂高频面试题-阻塞队列(上)_阻塞队列_64


运行结果会按照5-10-15的次数进行打印。