概念、生产者消费者模式
1)当队列满的时候,插入元素的线程被阻塞,直达队列不满。
2)队列为空的时候,获取元素的线程被阻塞,直到队列不空。
生产者和消费者模式
生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,便有了生产者和消费者模式。生产者和消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通信,而是通过阻塞队列来进行通信,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
常用方法
方法 | 抛出异常 | 返回值 | 一直阻塞 | 超时退出 |
插入方法 | add | offer | put | Offer(time) |
移除方法 | remove | poll | take | Poll(time) |
检查方法 | element | peek | N/A | N/A |
- 抛出异常:当队列满时,如果再往队列里插入元素,会抛出IllegalStateException("Queuefull")异常。当队列空时,从队列里获取元素会抛出NoSuchElementException异常。
- 返回特殊值:当往队列插入元素时,会返回元素是否插入成功,成功返回true。如果是移除方法,则是从队列里取出一个元素,如果没有则返回null。
- 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到队列可用或者响应中断退出。当队列空时,如果消费者线程从队列里take元素,队列会阻塞住消费者线程,直到队列不为空。
- 超时退出:当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程一段时间,如果超过了指定的时间,生产者线程就会退出。
常用阻塞队列
先区别一下有界队列和无界队列
有界队列:就是有固定大小的队列。比如设定了固定大小的 LinkedBlockingQueue,又或者大小为 0,只是在生产者和消费者中做中转用的 SynchronousQueue。
无界队列:指的是没有设置固定大小的队列。这些队列的特点是可以直接入列,直到溢出。当然现实几乎不会有到这么大的容量(超过 Integer.MAX_VALUE),所以从使用者的体验上,就相当于 “无界”。
比如没有设定固定大小的LinkedBlockingQueue。就是无界队列。
·ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
按照先进先出原则,要求设定初始大小
·LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
按照先进先出原则,可以不设定初始大小,Integer.Max_Value
ArrayBlockingQueue和LinkedBlockingQueue不同:
- 锁上面:ArrayBlockingQueue只有一个锁,LinkedBlockingQueue用了两个锁,
- 实现上:ArrayBlockingQueue直接插入元素,LinkedBlockingQueue需要转换。
·PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
默认情况下,按照自然顺序,要么实现compareTo()方法,指定构造参数Comparator
·DelayQueue:一个使用优先级队列实现的无界阻塞队列。
支持延时获取的元素的阻塞队列,元素必须要实现Delayed接口。适用场景:实现自己的缓存系统,订单到期,限时支付等等。
·SynchronousQueue:一个不存储元素的阻塞队列。
每一个put操作都要等待一个take操作
·LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
transfer(),必须要消费者消费了以后方法才会返回,tryTransfer()无论消费者是否接收,方法都立即返回。
·LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
可以从队列的头和尾都可以插入和移除元素,实现工作密取,方法名带了First对头部操作,带了last从尾部操作,另外:add=addLast; remove=removeFirst; take=takeFirst
阻塞队列的实现原理
比如,ArrayBlockingQueue就是基于Lock和Condition实现的。
使用DelayQueue 实现生产者与消费者模式:
订单实体类:
package com.caojiulu;
/**
*@author caojiulu
*
*类说明:订单的实体类
*/
public class Order {
private final String orderNo;//订单的编号
private final double orderMoney;//订单的金额
public Order(String orderNo, double orderMoney) {
super();
this.orderNo = orderNo;
this.orderMoney = orderMoney;
}
public String getOrderNo() {
return orderNo;
}
public double getOrderMoney() {
return orderMoney;
}
}
package com.caojiulu;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
*@author caojiulu
*
*类说明:存放到队列的元素
*/
public class ItemVo<T> implements Delayed{
private long activeTime;//到期时间,单位毫秒
private T date;
//activeTime是个过期时长
public ItemVo(long activeTime, T date) {
super();
this.activeTime = TimeUnit.NANOSECONDS.convert(activeTime,
TimeUnit.MILLISECONDS)+System.nanoTime();//将传入的时长转换为超时的时刻
this.date = date;
}
public long getActiveTime() {
return activeTime;
}
public T getDate() {
return date;
}
//按照剩余时间排序
@Override
public int compareTo(Delayed o) {
long d = getDelay(TimeUnit.NANOSECONDS)-o.getDelay(TimeUnit.NANOSECONDS);
return (d==0)?0:((d>0)?1:-1);
}
//返回元素的剩余时间
@Override
public long getDelay(TimeUnit unit) {
long d = unit.convert(this.activeTime-System.nanoTime(),
TimeUnit.NANOSECONDS);
return d;
}
}
生产者:
package com.caojiulu;
import java.util.concurrent.DelayQueue;
/**
*@author caojiulu
*
*类说明:将订单放入队列
*/
public class PutOrder implements Runnable {
private DelayQueue<ItemVo<Order>> queue;
public PutOrder(DelayQueue<ItemVo<Order>> queue) {
super();
this.queue = queue;
}
@Override
public void run() {
//5秒到期
Order ordeTb = new Order("Tb12345",366);
ItemVo<Order> itemTb = new ItemVo<Order>(5000,ordeTb);
queue.offer(itemTb);
System.out.println("订单5秒后到期:"+ordeTb.getOrderNo());
//8秒到期
Order ordeJd = new Order("Jd54321",366);
ItemVo<Order> itemJd = new ItemVo<Order>(8000,ordeJd);
queue.offer(itemJd);
System.out.println("订单8秒后到期:"+ordeJd.getOrderNo());
}
}
消费者:
package com.caojiulu;
import java.util.concurrent.DelayQueue;
/**
*@author caojiulu
*
*类说明:取出到期订单的功能
*/
public class FetchOrder implements Runnable {
private DelayQueue<ItemVo<Order>> queue;
public FetchOrder(DelayQueue<ItemVo<Order>> queue) {
super();
this.queue = queue;
}
@Override
public void run() {
while(true) {
try {
ItemVo<Order> item = queue.take();
Order order = (Order)item.getDate();
System.out.println("get from queue:"+order.getOrderNo());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类:
package com.caojiulu;
import java.util.concurrent.DelayQueue;
/**
*@author caojiulu
*
*类说明:测试延时订单
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
DelayQueue<ItemVo<Order>> queue = new DelayQueue<>();
new Thread(new PutOrder(queue)).start();
new Thread(new FetchOrder(queue)).start();
//每隔500毫秒,打印个数字
for(int i=1;i<15;i++){
Thread.sleep(500);
System.out.println(i*500);
}
}
}