Java_阻塞队列BlockingQueue接口和实现类
(一)什么是BlockingQueue?
collection接口------>Queue接口------>BlockingQueue接口----->7个实现类
- 当阻塞队列是空时,从队列中获取元素的操作将会被阻塞
- 当阻塞队列是满时,往队列里添加元素的操作将会被阻塞
想想 蛋糕店摆出十个蛋糕 卖出了才继续做 不浪费
(二)7个实现类
collection接口------>Queue接口------>BlockingQueue接口----->7个实现类
ArrayBlockingQueue【*】:由数组结构组成的有界阻塞队列。
LinkedBlockingQueue【*】:由链表结构组成的有界(但默认大小值为Interger.MAX_VALUE)阻塞队列。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
DelayQueue:使用优先级队列实现的延迟无界阻塞队列。
SynchronousQueue【*】:不存储元素的阻塞队列,也即单个元素的队列。就好比高定,定一个做一个
LinkedTransferQueue:由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:由链表结构组成的双向阻塞队列。
(三)BlockingQueue核心方法
3.1 理论
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SvucoOT4-1572022147843)(C:\Users\LENOVO\Desktop\SGG大数据笔记\noteimgs\BlockingQueue核心方法.PNG)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ZR0stgt-1572022147844)(C:\Users\LENOVO\Desktop\SGG大数据笔记\noteimgs\BlockingQueue核心方法2.PNG)]
3.2 代码演示
用ArrayBlockingQueue实现类举例演示
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* collection接口------>Queue接口------>BlockingQueue接口----->7个实现类
* ArrayBlockingQueue:由**数组结构**组成的**有界阻塞队列**。
* LinkedBlockingQueue:由**链表结构**组成的**有界**(但默认大小值为Interger.MAX_VALUE)**阻塞队列**。
* SynchronousQueue:**不存储元素的阻塞队列**,也即单个元素的队列。就好比高定,定一个做一个。
*
* BlockingQueue常用的四组方法
*
*/
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
// method1();
// method2();
// method3();
method4();
}
/**
* method1
* 抛出异常
* add
* remove
* element
*/
public static void method1(){
//List list = new ArrayList();
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//add
System.out.println(blockingQueue.add("a"));//true
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
// System.out.println(blockingQueue.add("x")); //抛异常
//element 审查有没有元素和队首
System.out.println(blockingQueue.element()); //a
//remove
System.out.println(blockingQueue.remove());//a
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove()); //抛异常
}
/**
* method2
* 特殊值
* add
* remove
* element
*/
public static void method2(){
//List list = new ArrayList();
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//offer
System.out.println(blockingQueue.offer("a"));//true
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("x")); //false
//offer 审查有没有元素和队首
System.out.println(blockingQueue.peek()); //a
//poll
System.out.println(blockingQueue.poll()); //a
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll()); //null
}
/**
* method3
* 阻塞
* put
* take
*/
public static void method3() throws InterruptedException {
//List list = new ArrayList();
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//put
blockingQueue.put("a");//无返回
blockingQueue.put("b");
blockingQueue.put("c"); //这步后就会阻塞,直到取走
// blockingQueue.put("x"); //
System.out.println("--------");
//take
blockingQueue.take();
blockingQueue.take();
blockingQueue.take(); //3生产 3消费 程序会正常退出
// blockingQueue.take(); //3生产 4消费 程序会阻塞 等待生产
}
/**
* method4
* 超时
* offer(e,time,unit)
* poll(time,unit)
*/
public static void method4() throws InterruptedException {
//List list = new ArrayList();
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//offer(e,time,unit)
System.out.println(blockingQueue.offer("a",2L, TimeUnit.SECONDS));//true
System.out.println(blockingQueue.offer("b",2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("c",2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("x",2L,TimeUnit.SECONDS)); //false
//poll(time,unit)
System.out.println(blockingQueue.poll(2L,TimeUnit.SECONDS)); //a
System.out.println(blockingQueue.poll(2L,TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2L,TimeUnit.SECONDS));
System.out.println(blockingQueue.poll(2L,TimeUnit.SECONDS)); //null
}
}
/*
-----method1
true
true
true
Exception in thread "main" java.lang.IllegalStateException: Queue full
-----method2
true
true
true
false
a
a
b
c
null
----method4
true
true
true
false
a
b
c
null
*/
(四)阻塞队列SynchronousQueue实现类演示
没有容量 你不消费 我不生产
每一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然
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<>();
//put
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"\tput 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName()+"\tput 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName()+"\tput 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"AAA").start();
//take
new Thread(()->{
try {
//暂停一会儿线程
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"BBB").start();
}
}
/*
AAA put 1
BBB 1
AAA put 2
BBB 2
AAA put 3
BBB 3
*/
(五)用在哪里
5.1 生产者消费者模式
5.1.1 传统版
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 传统版的生产者消费者模式,多线程消费,生产一个消费一个
*
* JUC中
* sync----lock
* wait----await
* notify----signal
*
* 多线程的判断必须要用while
*
*/
/**
* 资源类
*
* Condition它更强大的地方在于:
* 能够更加精细的控制多线程的休眠与唤醒。
* 对于同一个锁,我们可以创建多个Condition,
* 在不同的情况下使用不同的Condition
*
*/
class ShareData{
private int number = 0;
private Lock lock =new ReentrantLock();
private Condition condition = lock.newCondition();
//增加方法
public void increment() throws Exception{
lock.lock(); //ctrl+alt+T try-catch快捷键
try {
//1、判断
while (number != 0){
//等待 不能生产
condition.await();
}
//2、干活
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知唤醒
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//减少方法
public void decrement() throws Exception{
lock.lock(); //ctrl+alt+T try-catch快捷键
try {
//1、判断
while (number == 0){
//等待 不能生产
condition.await();
}
//2、干活
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知唤醒
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* 一个初始值为零的变量,两个线程交替操作,一个加一,一个减一
* 1 线程 操作(方法) 资源类
* 2 判断 干活 通知唤醒
* 3 防止虚假唤醒机制
*/
public class prodConsumer_TraditionDemo {
public static void main(String[] args) {
ShareData shareData = new ShareData();
for (int i=0;i<5;i++) {
new Thread(() -> {
try {
shareData.increment(); //调用增加方法
} catch (Exception e) {
e.printStackTrace();
}
},"AA").start();
}
for (int i=0;i<5;i++) {
new Thread(() -> {
try {
shareData.decrement(); //调用减少方法
} catch (Exception e) {
e.printStackTrace();
}
},"BB").start();
}
}
}
/*
AA 1
BB 0
AA 1
BB 0
AA 1
BB 0
AA 1
BB 0
AA 1
BB 0
*/
5.1.2 阻塞队列版
【sychronized和lock的区别】
/**
* sychronized和lock的区别
*
* 1 原始构成
* sync关键字属于JVM层面,
* monitorenter(底层是通过moniotr对象来完成,其实wait、notify
* 等方法也依赖于monitor对象,只有在同步块或方法中才能调用wait/
* notify等方法)
* monitorexit
* lcok是具体类(java.util.concurrent.locks.Lock)是api层面的锁
*
* 2 使用方法
* sync不需要手动去释放锁,当sync代码执行完成后系统会自动让线程释放
* 对锁的占用
* ReentrantLock则需要用户手动释放锁,如果没有手动释放锁,可能会出现
* 死锁现象。需要lock()和unlock()方法配合try/finally语句块来完成。
*
* 3 等待可否中断
* sync不可中断,除非抛出异常或者正常运行完成
* ReentrantLock 可中断,1.设置超时方法
* tryLock(long timeout,TimeUnit unit)
* 2. lcokInterruptibly()放代码块中,调用
* interrupt()方法可中断
*
* 4 加锁是否公平
* sync非公平锁
* ReentrantLock两者都可以,默认非公平锁,构造方法中可以传入boolean值,
* true为公平锁,false为非公平锁
*
* 5 锁绑定多个条件Condition
* sync没有 要么随机唤醒一个线程,要么全部唤醒线程
* ReentrantLock用来实现分组唤醒的线程们,可以精确唤醒
*
*
* 精确唤醒的例子
* 题目:
* 多线程之间按顺序调用,实现A->B->C三个线程启动,要求如下
* AA打印3次,BB打印4次,CC打印5次
* 紧接着
* AA打印3次,BB打印4次,CC打印5次
* ...
* 来五轮
*
*/
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 资源类
* 1通知2 2通知3 3通知1
*/
class ShareResource{
private int number = 1; //A:1 B:2 C:3
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
public void print3(){
lock.lock();
try {
//1 判断
while (number !=1){
c1.await(); //private Condition c1 = lock.newCondition();
}
//2 干活
for (int i = 1; i <= 3; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
//3 通知
number = 2; //1通知2 2通知3 3通知1
c2.signal(); //只通知2 !!!!
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print4(){
lock.lock();
try {
//1 判断
while (number !=2){
c2.await(); //private Condition c1 = lock.newCondition();
}
//2 干活
for (int i = 1; i <= 4; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
//3 通知
number = 3; //2通知3
c3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print5(){
lock.lock();
try {
//1 判断
while (number !=3){
c3.await(); //private Condition c1 = lock.newCondition();
}
//2 干活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
//3 通知
number = 1; //3通知1
c1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class SyncAndReentrantLockDemo {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(()->{
for (int i = 0; i < 5 ; i++) {
shareResource.print3();
}
},"AAA").start();
new Thread(()->{
for (int i = 0; i < 5 ; i++) {
shareResource.print4();
}
},"BBB").start();
new Thread(()->{
for (int i = 0; i < 5 ; i++) {
shareResource.print5();
}
},"CCC").start();
}
}
/*
AAA 1
AAA 2
AAA 3
BBB 1
BBB 2
BBB 3
BBB 4
CCC 1
CCC 2
CCC 3
CCC 4
CCC 5
AAA 1
AAA 2
AAA 3
BBB 1
BBB 2
BBB 3
BBB 4
CCC 1
CCC 2
CCC 3
CCC 4
CCC 5
AAA 1
AAA 2
AAA 3
BBB 1
BBB 2
BBB 3
BBB 4
CCC 1
CCC 2
CCC 3
CCC 4
CCC 5
AAA 1
AAA 2
AAA 3
BBB 1
BBB 2
BBB 3
BBB 4
CCC 1
CCC 2
CCC 3
CCC 4
CCC 5
AAA 1
AAA 2
AAA 3
BBB 1
BBB 2
BBB 3
BBB 4
CCC 1
CCC 2
CCC 3
CCC 4
CCC 5
*/
5.2 线程池
后续补充
5.3 消息中间件