乐趣区

关于java:重温-JAVA-阻塞队列-终

BlockingQueue

BlockingQueue 接口定义了很多插入和删除的办法,这里总结梳理如下:

增加

  1. 容器满了,抛异样
    add(e)
  2. 返回是否胜利
    offer(e)
  3. 容器满了,阻塞
    put(e)
  4. 容器满了,期待超时退出
    offer(e, time, unit)

删除

  1. 容器没有元素,抛异样
    remove()
  2. 返回是否胜利
    remove(obj)
    poll()
  3. 容器没有元素,阻塞
    take()
  4. 容器没有元素,期待超时退出
    poll(time, unit)

其余

  1. 获取队头元素,但不出容器,没有则抛异样
    element()
  2. 获取队头元素,但不出容器,没有不抛异样
    peek()

总结

有界阻塞队列

ArrayBlockingQueue

  • 数组实现的有界阻塞队列;
  • 队列依照先进先出准则对元素进行排列;
  • 默认不保障线程偏心拜访队列;
  • 所谓偏心拜访队列,是指依照阻塞线程的程序拜访队列。

LinkedBlockingQueue

  • 链表实现的有界阻塞队列;
  • FIFO
  • 未设置容器大小,默认为 Integer.MAX_VALUE

SynchronousQueue

  • 不存储元素的有界阻塞队列
  • 每一个 put 操作,必须对应另外一个线程的 take 操作
  • 队列 实现偏心拜访;用 实现不偏心拜访

LinkedBlockingDequeue

  • 双向链表实现的有界阻塞队列
  • 双向链表指的是:可从队头、队尾 插入或删除元素
  • 未设置容器大小,默认为 Integer.MAX_VALUE

共性

  • put 操作在容器满时,会阻塞
  • take 操作在容器没有元素时,会阻塞

无界阻塞队列

PriorityBlockingQueue

  • 基于 实现的具备 优先级 的无界阻塞队列
  • 默认依照天然排序升序排列
  • 可通过元素的外部排序,或内部排序自定义程序

DelayQueue

  • 外部基于 PriorityQueue 实现反对延时获取元素的无界阻塞队列
  • 队列内元素必须实现 Delayed 接口

LinkedTransferQueue

  • 链表实现的无界阻塞队列
  • 与其余阻塞队列不同的是,多提供了 transfer, tryTransfer 办法
  • transfer 会阻塞到有消费者从队列中生产元素
  • tryTransfer 不带工夫的办法,会间接返回;带工夫的办法,会依据工夫阻塞到有消费者生产

总结

  • put, take 均不阻塞

阻塞队列的道与术

下面讲了阻塞队列如何应用,然而那仅仅是术。只有理解了道(阻塞队列的设计),咱们才能够在即便是换了语言的状况下,也能模拟 JAVA 阻塞队列的设计思路,本人编写阻塞队列。

问题一:抉择适合的数据结构

数据结构,选数组,还是链表都能够。只有分明数组,链表各自的优缺点,并进行抉择即可。
思考到须要高速存取,不心愿有内存碎片。因而,抉择数组会绝对适合。在思考到容器的重复使用,咱们就须要 2 个指针,寄存以后消费者生产到哪个地位,生产者最初生产的元素在哪个地位。

其实以上形容的是循环数组的构造。

问题二:容器满了,生产者如何实现不在生产;容器没元素,消费者如何实现不再生产

这里用 伪代码 代码形容该问题。

public void producer() {while(数组长度 == 容器内的元素个数) {生产者休眠}
}
public void consumer() {while(容器内的元素个数 == 0) {消费者休眠}
}

问题三:生产者应该在什么时候唤醒消费者;消费者应该在什么时候唤醒生产者

将上诉代码略微革新下。咱们只须要在 producer 办法被调用后,就能够告诉消费者生产了

public void producer() {while(数组长度 == 容器内的元素个数) {生产者休眠}
    生产
    唤醒消费者
}
public void consumer() {while(容器内的元素个数 == 0) {消费者休眠}
    生产
    唤醒生产者
}

问题四:如何爱护共享资源

对于共享资源的爱护,那只能应用锁来爱护了

public void producer() {
加锁
    while(数组长度 == 容器内的元素个数) {生产者休眠}
    生产
    唤醒消费者
开释锁
}
public void consumer() {
加锁
    while(容器内的元素个数 == 0) {消费者休眠}
    生产
    唤醒生产者
开释锁
}

总结

上诉的代码其实是十分经典的多生产者与多消费者的代码。这边做的革新,仅仅是应用循环数组作为存储容器。

参考

  • 聊聊 Java 中的并发队列中 有界队列和无界队列的区别
  • 《Java 并发编程的艺术》
退出移动版