1、阻塞队列知道吗?
- 阻塞队列是一个队列,在数据结构中起的作用如下图:
线程1往阻塞队列里添加元素,线程2从阻塞队列里移除元素
-
当队列是空的,从队列中获取元素的操作将会被阻塞
-
当队列是满的,从队列中添加元素的操作将会被阻塞
-
试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素,同样试图向已满的队列中添加新元素的线程将会被阻塞,直到其他线程从队列中移除一个或多个元素或者完全清空,使队列变得空闲起来并后续新增
1.1、使用阻塞队列有啥好处,有啥作用?
- 在多线程领域:所谓阻塞,在某些情况下会挂起线程(即线程阻塞),一旦条件满足,被挂起的线程优惠被自动唤醒
- 为什么需要使用BlockingQueue:好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为BlockingQueue都一手给你包办好了
- 在concurrent包发布以前,在多线程环境下,我们每个程序员都必须自己去控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。
1.2、BlockingQueue的一些核心方法
Throws exception(抛出异常) | Special value(特殊值) | Blocks(阻塞) | Times out(超时) | |
---|---|---|---|---|
Insert(插入) | add(e) | offer(e) | put(e) | offer(e, time, unit) |
Remove(移除) | remove() | poll() | take() | poll(time, unit) |
Examine(检查) | element() | peek() | not applicable | not applicable |
抛出异常 | 当阻塞队列满时,再往队列里add插入元素会抛IllegalStateException:Queue full 当阻塞队列空时,再往队列里remove移除元素会抛NoSuchElementException |
---|---|
特殊值 | 插入方法,成功ture失败false 移除方法,成功返回出队列的元素,队列里没有就返回null |
一直阻塞 | 当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产者线程直到put数据or响应中断退出 当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用 |
超时退出 | 当阻塞队列满时,队列会阻塞生产者线程一定时间,超过限时后生产者线程会退出 |
//案例使用
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//第一组
// System.out.println(blockingQueue.add("a"));
// System.out.println(blockingQueue.add("b"));
// System.out.println(blockingQueue.add("c"));
// System.out.println(blockingQueue.element()); //队列先进先出 得到元素a
//System.out.println(blockingQueue.add("x"));
// System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
// System.out.println(blockingQueue.remove());
// 第二组
// System.out.println(blockingQueue.offer("a"));
// System.out.println(blockingQueue.offer("b"));
// System.out.println(blockingQueue.offer("c"));
// System.out.println(blockingQueue.offer("x"));
// System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.poll());
// 第三组
// blockingQueue.put("a");
// blockingQueue.put("b");
// blockingQueue.put("c");
// //blockingQueue.put("x");
// System.out.println(blockingQueue.take());
// System.out.println(blockingQueue.take());
// System.out.println(blockingQueue.take());
// System.out.println(blockingQueue.take());
//第四组
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("a",3L, TimeUnit.SECONDS));
}
}
#第一组返回结果 当阻塞队列满时,再往队列里add插入元素会抛IllegalStateException:Queue full
true
true
true
a
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)
at com.song.BlockingQueueTest.BlockingQueueDemo.main(BlockingQueueDemo.java:26)
#第二组返回结果 ---》插入成功ture 阻塞队列满了就失败false 移除成功返回出队列的元素,没有就返回null
true
true
true
false
a
b
c
null
#第三组返回结果 ---》一直阻塞 直到ok
#第四组返回结果 ---》当阻塞队列满时,队列会阻塞生产者线程一定时间,超过限时后生产者线程会退出
true
true
true
false
1.3、BlockingQueue架构介绍
-
ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
-
LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列。
-
PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
-
DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
-
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
- SynchronousQueue没有容量与其他BlcokingQueue不同,SynchronousQueue是一个不存储元素的BlcokingQueue。每个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然.
-
LinkedTransferQueue:由链表组成的无界阻塞队列。
-
LinkedBlockingDeque:由链表组成的双向阻塞队列。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
*SynchronousQueue阻塞队列 案例
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "\t put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName() + "\t put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName() + "\t put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "AAA").start();
new Thread(() -> {
try {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take());
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t" + blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "BBB").start();
}
}
#返回结果 -----》put一个必须take一个 才能执行下一个
AAA put 1
BBB 1
AAA put 2
BBB 2
AAA put 3
BBB 3
Process finished with exit code 0
1.4、在哪里用到过?
- 生产者消费者模式
- 线程池
- 消息中间件
package com.song.BlockingQueueTest;
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; //默认开启,进行生产+消费
private AtomicInteger atomicInteger = new AtomicInteger();
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 retValue ; //默认是false
while (FLAG)
{
//往阻塞队列填充数据
data = atomicInteger.incrementAndGet()+"";//等于++i的意思
retValue = blockingQueue.offer(data,2L, TimeUnit.SECONDS);
if (retValue){ //如果是true,那么代表当前这个线程插入数据成功
System.out.println(Thread.currentThread().getName()+"\t插入队列"+data+"成功");
}else { //那么就是插入失败
System.out.println(Thread.currentThread().getName()+"\t插入队列"+data+"失败");
}
TimeUnit.SECONDS.sleep(1);
}
//如果FLAG是false了,马上打印
System.out.println(Thread.currentThread().getName()+"\t大老板叫停了,表示FLAG=false,生产结束");
}
//消费者
public void MyConsumer() throws Exception
{
String result = null;
while (FLAG) { //开始消费
//两秒钟等不到生产者生产出来的数据就不取了
result = blockingQueue.poll(2L,TimeUnit.SECONDS);
if (null == result || result.equalsIgnoreCase("")){ //如果取不到数据了
FLAG = false;
System.out.println(Thread.currentThread().getName()+"\t 超过两秒钟没有取到数据,消费退出");
System.out.println();
System.out.println();
return;//退出
}
System.out.println(Thread.currentThread().getName()+"\t消费队列数据"+result+"成功");
}
}
//叫停方法
public void stop() throws Exception{
this.FLAG = false;
}
}
public class ProdConsumer_BlockQueueDemo {
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 消费线程启动");
System.out.println();
System.out.println();
try {
myResource.MyConsumer();
System.out.println();
System.out.println();
} catch (Exception e) {
e.printStackTrace();
}
},"Consumer").start();
try { TimeUnit.SECONDS.sleep(5); }catch (Exception e) {e.printStackTrace();}
System.out.println();
System.out.println();
System.out.println();
System.out.println("5秒钟时间到,大bossMain主线程叫停,活动结束");
myResource.stop();
}
}