介绍
java.util.concurrent包中的BlockingQueue接口通常用于一个线程生产对象,而另一个线程消费这些对象的场景。
一个线程将会持续生产新对象并将其插入队列中,直到队列达到所能容纳的临界点。也就是说,队列是有限的。如果该阻塞队列到达了其临界点,负责生产的线程将会发生阻塞,且一直处于阻塞状态中,直到负责消费的线程从队列中拿走一个对象。负责消费的线程会一直从阻塞队列中拿出对象。如果消费线程尝试从一个空队列中提取对象的话,这个线程将会处于阻塞状态中,知道一个生产线程把一个对象放进队列。
种类
BlockQueue是一个接口,你需要使用它的实现之一来使用,具体的实现有:
- ArrayBlockingQueue:数组结构组成的有界阻塞队列
- DelayQueue:使用优先级队列实现的延迟无界阻塞队列
- LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列
- PriorityBlockingQueue:支持优先级排序的无界阻塞队列
- SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
- LinkedTransferQueue:由链表组成的无界阻塞队列
- LinkedBlockingDeque:由链表组成的双向阻塞队列
核心方法
方法类型 | 抛出异常 | 特殊值 | 阻塞 |
---|---|---|---|
插入 | add(e) | offer(e) | put(e) |
移除 | remove() | poll() | take() |
检查 | element() | peek() | 不可用 |
- 抛出异常:当阻塞队列满时,再往队列里add插入元素会抛IllegalStateException:Queue full
当阻塞队列空时,再往队列里remove移除元素会抛NoSuchElementException - 特殊值:插入方法,成功ture失败false,移除方法,成功返回出队列的元素,队列里没有就返回null
- 阻塞:当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产者线程直到put数据or响应中断退出,当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用
- 超时退出:当阻塞队列满时,队列会阻塞生产者线程一定时间,超过限时后生产者线程会退出
demo
模拟消息中间件发短信
@WebListener
public class SendCodeListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
// Queue<String> phones = new LinkedList<String>();
// 换成阻塞队列
BlockingQueue<String> phones = new LinkedBlockingDeque<String>();
sce.getServletContext().setAttribute("phones", phones);
// 开启一个 线程
new Thread(){
@Override
public void run() {
while(true) {
String phone;
try {
phone = phones.take();// 阻塞式 取不到值 阻塞
System.out.println("队列取出"+phone);
if(phone != null) {
System.out.println(phone+"---->短信发送成功");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
}
/**
* Servlet implementation class PhoneServlet
*/
@WebServlet("/PhoneServlet")
public class PhoneServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public PhoneServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String phone = request.getParameter("phone");
BlockingQueue<String> phones = (BlockingQueue<String>) request.getServletContext().getAttribute("phones");
try {
// 加入队列
phones.put(phone);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
response.getWriter().write("ok");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
生产者-消费者模式
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class MyResource {
/**
* 默认开启 进行生产消费的交互
*/
private volatile boolean flag = true;
/**
* 默认值是0
*/
private AtomicInteger atomicInteger = new AtomicInteger();
private BlockingQueue<String> blockingQueue = null;
public MyResource(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
System.out.println(blockingQueue.getClass().getName());
}
public void myProd() throws Exception {
String data = null;
boolean returnValue;
while (flag) {
data = atomicInteger.incrementAndGet() + "";
returnValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
if (returnValue) {
System.out.println(Thread.currentThread().getName() + "\t 插入队列数据" + data + "成功");
} else {
System.out.println(Thread.currentThread().getName() + "\t 插入队列数据" + data + "失败");
}
TimeUnit.SECONDS.sleep(1);
}
System.out.println(Thread.currentThread().getName() + "\t 停止 表示 flag" + flag);
}
public void myConsumer() throws Exception {
String result = null;
while (flag) {
result = blockingQueue.poll(2L, TimeUnit.SECONDS);
if(null==result||"".equalsIgnoreCase(result)){
flag=false;
System.out.println(Thread.currentThread().getName()+"\t"+"超过2m没有取到 消费退出");
return;
}
System.out.println(Thread.currentThread().getName() + "消费队列" + result + "成功");
}
}
public void stop() throws Exception{
flag=false;
}
}
public class ProdConsumerBlockQueueDemo {
public static void main(String[] args) throws Exception {
MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10));
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t生产线程启动");
try {
myResource.myProd();
} catch (Exception e) {
e.printStackTrace();
}
},"Prod").start();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t消费线程启动");
try {
myResource.myConsumer();
} catch (Exception e) {
e.printStackTrace();
}
},"consumer").start();
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("时间到,停止活动");
myResource.stop();
}
}