Java并发编程-BlockingQueue阻塞队列

1.BlockingQueue介绍

1.1.引言:

阻塞虽然是不好的,但是我们有时候不得不阻塞,比如说一家餐厅人满了,店主当然不希望即将过来的这些客人全部都走,而是希望它们留下来继续排队等候。对于阻塞的内容,线程和资源的调度管理能力,这样就出了一个接口,就叫阻塞队列。在不得不阻塞,必须要阻塞的时候,如何管理好被阻塞的资源和线程,这就是阻塞队列。

队列的特性:先进先出,插入元素在队尾,删除元素在队头。

1.2.阻塞队列介绍:

阻塞:必须要阻塞/不得不阻塞
阻塞队列是一个队列,在数据结构中中起的作用如下图:
在这里插入图片描述
线程1往阻塞队列里添加元素,线程2从阻塞队列里移除元素。
阻塞队列,我们可以简单的理解为海底捞火锅店的候客区,如图绿框框就是我们的阻塞队列,即我们所理解的海底捞火锅店的候客区,里面等着的就是一桌一桌的食客。如果候客区也满了,还加不加人?
当队列是空的,从队列中获取元素的操作将会被阻塞。
当队列是满的,从队列中添加元素的操作将会被阻塞。
试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素。

对于上面阻塞的问题,我们又可以举一个例子,假如说Thread1是做蛋糕的师傅,Thread2是买蛋糕的顾客,而BlockingQueue是食品柜。当顾客来了,发现食品柜是空的,想消费蛋糕的操作将会被阻塞。如果蛋糕买的人少了,食品柜中的蛋糕满了,此时生产蛋糕的操作将会被阻塞。

**试图从空的队列中获取元素的线程将会被阻塞,直到其他线程往空的队列插入新的元素。**简单的说,就是空的时候不能消费,满的时候不能增加。
试图向已满的队列中添加新元素的线程将会被阻塞,直到其它线程从队列中移除一个或多个元素或者完全清空,使队列变得空闲起来并后续新增。

1.3.阻塞队列的用处:

在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤起。
wait就是一种阻塞,notify是一种唤醒,await是一种阻塞, signal是唤醒。

为什么需要 BlockingQueue?
好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue 都给你一手包办了。在 concurrent 包发布以前,在多线程环境下,我们每个程序员都必须自己去控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。
也就是不用手写wait和notify了。只要柜子里蛋糕满了,生产线程就会阻塞,只要柜子里没有蛋糕了,消费线程就会被阻塞。

1.4.接口架构图

在这里插入图片描述
BlockingQueue所有已知实现类:
ArrayBlockingQueue , DelayQueue , LinkedBlockingDeque , LinkedBlockingQueue , LinkedTransferQueue , PriorityBlockingQueue , SynchronousQueue

在我们以前学习集合的过程中,Collection接口有两个常用的接口List接口和Set接口,现在又有了阻塞队列接口BlockingQueue

ArrayBlockingQueue:由数组结构组成的有界阻塞队列
LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列
PriorityBlockingQueue:支持优先级排序的无界阻塞队列
DelayQueue:使用优先级队列实现的延迟无界阻塞队列
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
LinkedTransferQueue:由链表结构组成的无界阻塞队列
LinkedBlockingDeque:由链表结构组成的双向阻塞队列

2.BlockingQueue核心方法

核心方法如下:

方法类型 抛出异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e,time,unit)
移除 remove() poll() take() put(time,unit)
移除 element() peek() 不可用 不可用

共同点:插入成功或者删除成功都返回true(除了put和take),区别在于处理异常情况

(1)抛出异常的方法
当阻塞队列满时,再往队列里add元素会抛出IllegalStateException:Queue full;
当阻塞队列空时,再从队列里remove移除元素会抛NoSuchElementException。

(2)返回特殊值(boolean值)的方法
插入成功,返回true;插入失败,返回false;
删除成功返回出队列元素;删除失败返回null;

(3)阻塞的方法(添加无返回值)
当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产线程直到put数据or响应中断退出。
当阻塞队列空时,消费者线程试图take队列里的元素,队列会一直阻塞消费者线程直到队列有可用元素。

(4)超时的方法
当向阻塞队列offer元素时候,时间超过了设定的值,就会出现超时中断;
当向阻塞队列poll元素时候,时间超过了设定的值,就会出现超时中断。

3.BlockingQueue案例测试

3.1.抛出异常的方法代码验证:

  • add方法抛出异常
	public static void main(String[] args) {
    
    
		//ArrayBlockingQueue是由数组结构组成的有界阻塞队列,所以要加参数
		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.add("d"));
	}

在这里插入图片描述

public static void main(String[] args) {
    
    
		//ArrayBlockingQueue是由数组结构组成的有界阻塞队列,所以要加参数
		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.add("d"));
	}

在这里插入图片描述
当阻塞队列满时,再往队列里add元素会抛出IllegalStateException:Queue full;

  • remove方法抛出异常
public static void main(String[] args) {
    
    
		//ArrayBlockingQueue是由数组结构组成的有界阻塞队列,所以要加参数
		BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);
		System.out.println(blockingQueue.add("a"));
		System.out.println(blockingQueue.add("b"));
		System.out.println(blockingQueue.add("c"));	
		//remove会返回被移除的对象
		System.out.println(blockingQueue.remove());
		System.out.println(blockingQueue.remove());
		System.out.println(blockingQueue.remove());
		System.out.println(blockingQueue.remove());
}

在这里插入图片描述
当阻塞队列空时,再从队列里remove移除元素会抛NoSuchElementException。

  • element检查方法:(底层调用了 peek()方法)
public static void main(String[] args) {
    
    
		//ArrayBlockingQueue是由数组结构组成的有界阻塞队列,所以要加参数
		BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);
		System.out.println(blockingQueue.add("a"));
		System.out.println(blockingQueue.add("a"));
		//element就是查看一下队首元素的值
		System.out.println(blockingQueue.element());
	}

在这里插入图片描述

3.2.返回特殊值的方法(boolean值返回)

  • 阻塞队列offer方法的使用
public static void main(String[] args) {
    
    
		BlockingQueue<String> 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("d"));
}

在这里插入图片描述

插入成功,返回true;插入失败,返回false;

  • 阻塞队列poll方法的使用
public static void main(String[] args) {
    
    
		BlockingQueue<String> 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.poll());
		System.out.println(blockingQueue.poll());
		System.out.println(blockingQueue.poll());
		System.out.println(blockingQueue.poll());
}

在这里插入图片描述
删除成功返回出队列元素;删除失败返回null;

3.3.阻塞队列的方法

  • 阻塞队列put方法使用
public static void main(String[] args) throws InterruptedException {
    
    
		BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);
		blockingQueue.put("a");
		blockingQueue.put("a");
		blockingQueue.put("a");
		blockingQueue.put("a");
	}

在这里插入图片描述
没有报错,当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产线程直到put数据or响应中断退出。,即腾出空位置为止,能塞进去。与semaphore争车位类似

  • 阻塞队列take方法使用
public static void main(String[] args) throws InterruptedException {
    
    
		BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);
		blockingQueue.put("a");
		blockingQueue.put("a");
		blockingQueue.put("a");
		System.out.println(blockingQueue.take());
		System.out.println(blockingQueue.take());
		System.out.println(blockingQueue.take());
		System.out.println(blockingQueue.take());
	}

在这里插入图片描述
当阻塞队列空时,消费者线程试图take队列里的元素,队列会一直阻塞消费者线程直到队列有可用元素。

3.4.超时中断的方法

  • 阻塞队列offer超时中断演示
public static void main(String[] args) throws InterruptedException {
    
    
		BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);
		System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));
		System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));
		System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));
		System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));
	}

程序执行结果如下:当往阻塞队列添加第六个元素的时候,队列添加不进去,3秒后会超时中断。
在这里插入图片描述
当向阻塞队列offer元素时候,时间超过了设定的值,就会出现超时中断;

  • 阻塞队列poll超时中断演示
public static void main(String[] args) throws InterruptedException {
    
    
		BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<>(3);
		System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));
		System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));
		System.out.println(blockingQueue.offer("a",3L,TimeUnit.SECONDS));
		System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));
		System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));
		System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));
		System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));
	}

当向阻塞队列poll元素时候,时间超过了设定的值,就会出现超时中断。

猜你喜欢

转载自blog.csdn.net/qq_39736597/article/details/113356559