参考:
http://ifeve.com/blocking-queues/
https://blog.csdn.net/bohu83/article/details/80784891
1.阻塞队列
[1].当阻塞队列是空时,从队列中获取元素的操作将会被阻塞,直到其他线程往里面插入新的元素
[2].当阻塞队列是满时,往队列里添加元素的操作将会被阻塞,直到其他线程从队列里移除一个或多个或者完全清空再继续添加
2.好处
[1].在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒
[2].使用BlockingQueue的好处
我们不在需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都处理好了,在concurrent包发布前,在多线程环境下,程序员需要手动控制这些细节,还有兼顾安全和效率
3.架构体系
[1].ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
[2].LinkedBlockingQueue :一个由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列。
[3].PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
[4].DelayQueue:一个使用优先级队列实现的无界阻塞队列。
[5].SynchronousQueue:一个不存储元素的阻塞队列,也即单个元素的队列。
[6].LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
[7].LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列
4.常用方法
方法\处理方式 |
抛出异常 |
返回特殊值 |
一直阻塞 |
超时退出 |
插入方法 |
add(e) |
offer(e) |
put(e) |
offer(e,time,unit) |
移除方法 |
remove() |
poll() |
take() |
poll(time,unit) |
检查方法 |
element() |
peek() |
不可用 |
不可用 |
[1].抛出异常:
①.是指当阻塞队列满时候,再往队列里插入元素,会抛出IllegalStateException(“Queue full”)异常。
②.当队列为空时,从队列里获取元素时会抛出NoSuchElementException异常 。
package com.w4xj.interview.thread;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* @Author by w4xj
* @Classname ThrowExceptionBlockingQueueFunction
* @Description TODO
* @Date 2019/5/8 12:58
* @Created by IDEA
*/
public class ThrowExceptionBlockingQueueMethod {
public static void main(String[] args) {
BlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//Exception in thread "main" java.lang.IllegalStateException: Queue full
//System.out.println(blockingQueue.add("z"));
//队列头元素
blockingQueue.element();
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//java.util.NoSuchElementException
//System.out.println(blockingQueue.remove());
}
}
[2].返回特殊值:
①.插入方法会返回是否成功,成功则返回true。
②.移除方法,则是从队列里拿出一个元素,如果没有则返回null
package com.w4xj.interview.thread;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* @Author by w4xj
* @Classname ReturnSpecialValueBlockingQueueMethod
* @Description TODO
* @Date 2019/5/8 20:05
* @Created by IDEA
*/
public class ReturnSpecialValueBlockingQueueMethod {
public static void main(String[] args) {
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("z"));
System.out.println("-----------------------------------------");
System.out.println(blockingQueue.peek());
System.out.println("-----------------------------------------");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
/*
打印:
true
true
true
false
-----------------------------------------
a
-----------------------------------------
a
b
c
null
*/
}
}
[3].一直阻塞:
①.当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。
②.当队列空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。
[4].超时退出:
①.当阻塞队列满时,队列会阻塞生产者线程一段时间,
②.如果超过一定的时间,生产者线程就会退出。
package com.w4xj.interview.thread;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* @Author by w4xj
* @Classname TimeBlockingQueue
* @Description TODO
* @Date 2019/5/8 20:19
* @Created by IDEA
*/
public class TimeBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
System.out.println(blockingQueue.offer("a", 2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("b", 2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("c", 2L, TimeUnit.SECONDS));
System.out.println(blockingQueue.offer("z", 2L, TimeUnit.SECONDS));
/*
打印:
true
true
true
false
*/
}
}
5.SynchronousQueue
[1].SynchronousQueue没有容量,与其他BlockingQueue不同,SynchronousQueue是一个不存储元素的SynchronousQueue。没一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然
[2].代码示例
package com.w4xj.interview.thread;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* @Author by w4xj
* @Classname SynchronousQueueTest
* @Description TODO
* @Date 2019/5/8 20:37
* @Created by IDEA
*/
public class SynchronousQueueTest {
public static void main(String[] args) {
BlockingQueue blockingQueue = new SynchronousQueue<>();
new Thread(() ->{
try {
System.out.println(Thread.currentThread().getName() + " put a");
blockingQueue.put("a");
System.out.println(Thread.currentThread().getName() + " put b");
blockingQueue.put("b");
System.out.println(Thread.currentThread().getName() + " put c");
blockingQueue.put("c");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"ThreadA").start();
new Thread(() ->{
try {
//线程睡1秒
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + " get " + blockingQueue.take());
//线程睡1秒
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + " get " + blockingQueue.take());
//线程睡1秒
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + " get " + blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"ThreadB").start();
/*
打印:
ThreadA put a
ThreadB get a
ThreadA put b
ThreadB get b
ThreadA put c
ThreadB get c
*/
}
}
6.生产消费者
[1].使用synchronized
[2].使用Lock(Lock和synchronized的对比,见Java锁)
package com.w4xj.interview.thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author by w4xj
* @Classname ProducerConsumerTraditional
* @Description TODO
* @Date 2019/5/8 22:14
* @Created by IDEA
*/
public class ProducerConsumerTraditional {
public static void main(String[] args) {
/*
线程操作资源类
判断干活唤醒
防止虚假唤醒
*/
Resource resource = new Resource();
new Thread(() ->{
try {
for (int i = 0; i < 5; i++) {
resource.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"ThreadA").start();
new Thread(() ->{
try {
for (int i = 0; i < 5; i++) {
resource.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"ThreadB").start();
/*
打印:
ThreadA 1
ThreadB 0
ThreadA 1
ThreadB 0
ThreadA 1
ThreadB 0
ThreadA 1
ThreadB 0
ThreadA 1
ThreadB 0
*/
}
}
class Resource{
private int number = 0;
private Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
//while防止虚假唤醒
while (number != 0) {
condition.await();
}
number++ ;
System.out.println(Thread.currentThread().getName() + "\t" + number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
//while防止虚假唤醒
while (number == 0) {
condition.await();
}
number-- ;
System.out.println(Thread.currentThread().getName() + "\t" + number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
[3].使用阻塞队列
package com.w4xj.interview.thread;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author by w4xj
* @Classname ProducerConsumerBlockingQueue
* @Description TODO
* @Date 2019/5/10 7:38
* @Created by IDEA
*/
public class ProducerConsumerBlockingQueue {
public static void main(String[] args) {
CakeResouce cakeResouce = new CakeResouce(new ArrayBlockingQueue(3));
new Thread(() ->{
try {
cakeResouce.produce();
} catch (Exception e) {
e.printStackTrace();
}
},"prodcuder").start();
new Thread(() ->{
try {
cakeResouce.consume();
} catch (Exception e) {
e.printStackTrace();
}
},"consumer").start();
//线程睡1秒
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
cakeResouce.stopProduce();
/*
打印:
prodcuder 生产1成功
consumer 消费1成功
consumer 消费2成功
prodcuder 生产2成功
consumer 消费3成功
prodcuder 生产3成功
prodcuder 生产4成功
consumer 消费4成功
prodcuder 生产5成功
consumer 消费5成功
main 嘿,停止生产了~~
prodcuder 停止生产
consumer 两秒钟未能成功消费,退出
consumer 停止消费
*/
}
}
class CakeResouce{
/**
* 线程执行标志
* true:执行
* false:停止
*/
private volatile boolean currentFlag = true;
private AtomicInteger atomicInteger = new AtomicInteger();
private BlockingQueue blockingQueue = null;
/**
* 构造时传入队列实例
* @param blockingQueue
*/
public CakeResouce(BlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
/**
* 生产
* @throws Exception
*/
public void produce() throws Exception{
String data ;
while (currentFlag){
data = String.valueOf(atomicInteger.incrementAndGet());
boolean offer = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
if(offer){
System.out.println(Thread.currentThread().getName() + " \t生产" + data + "成功");
}else {
System.out.println(Thread.currentThread().getName() + " \t生产" + data + "失败");
}
//线程睡400ms
try { TimeUnit.MILLISECONDS.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); }
}
System.out.println(Thread.currentThread().getName() + "\t 停止生产");
}
/**
* 消费
* @throws Exception
*/
public void consume() throws Exception{
String data ;
while (currentFlag){
String poll = blockingQueue.poll(2L, TimeUnit.SECONDS);
if(null != poll && !"".equalsIgnoreCase(poll)){
System.out.println(Thread.currentThread().getName() + " \t消费" + poll + "成功");
}else {
currentFlag = false;
System.out.println(Thread.currentThread().getName() + " \t两秒钟未能成功消费,退出");
}
//线程睡100毫秒,防止消费x打印在生产x之前
try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
}
System.out.println(Thread.currentThread().getName() + "\t 停止消费");
}
/**
* 停止生产
*/
public void stopProduce(){
currentFlag = false;
System.out.println(Thread.currentThread().getName() + "\t 嘿,停止生产了~~");
}
}