这一节我们来了解阻塞队列(BlockingQueue),BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,当容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会阻塞。首先我们来看ArrayBlockingQueue和LinkedBlockingQueue.

ArrayBlockingQueue

ArrayBlockingQueue是一个用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。默认情况下不保证访问者公平的访问队列,所谓公平访问队列是指阻塞的所有生产者线程或消费者线程,当队列可用时,可以按照阻塞的先后顺序访问队列,即先阻塞的生产者线程,可以先往队列里插入元素,先阻塞的消费者线程,可以先从队列里获取元素。通常情况下为了保证公平性会降低吞吐量。

我们看他的构造函数实现:

//默认是非公平的,初始指定队列容量
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
} //该构造方法可以设置队列的公平性。当然如果为公平的,则对性能会产生影响
//访问者的公平性是使用可重入锁实现的
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}

使用很简单我们直接看一个实例:

public class ProducerConsumerTest {
public static void main(String[] args) { final BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(3); ExecutorService service = Executors.newFixedThreadPool(10); for(int i = 0;i<4;i++){
service.execute(new ProducerAndConsumer(blockingQueue));
}
}
} class ProducerAndConsumer implements Runnable{
private boolean flag = false; private Integer j = 1; private Lock lock = new ReentrantLock(); Condition pro_con = lock.newCondition(); Condition con_con = lock.newCondition(); private BlockingQueue<Integer> blockingQueue; public ProducerAndConsumer(BlockingQueue<Integer> blockingQueue){
this.blockingQueue= blockingQueue;
} //生产
public void put(){
try {
lock.lock();
while(flag)
pro_con.await();
System.out.println("正在准备放入数据。。。");
Thread.sleep(new Random().nextInt(10)*100);
Integer value = new Random().nextInt(30);
blockingQueue.put(value);
System.out.println(Thread.currentThread().getName()+" 放入的数据 "+value);
flag = true;
con_con.signal();
} catch (Exception e) {
e.printStackTrace();
}
finally{
lock.unlock();
}
} public void get(){
try {
lock.lock();
while(!flag)
con_con.await();
System.out.println("正在准备取数据。。。");
Thread.sleep(new Random().nextInt(10)*1000);
System.out.println(Thread.currentThread().getName()+" 取到的数据为"+blockingQueue.take());
flag = false;
pro_con.signal();
} catch (Exception e) {
e.printStackTrace();
}
finally{
lock.unlock();
}
} @Override
public void run() { while(true){
if(j==1){
put();
}
else{
get();
}
j=(j+1)%2;
}
}
}

输出为:

正在准备放入数据。。。
正在准备放入数据。。。
正在准备放入数据。。。
正在准备放入数据。。。
pool-1-thread-2 放入的数据 13
正在准备取数据。。。
pool-1-thread-3 放入的数据 4
正在准备取数据。。。
pool-1-thread-3 取到的数据为13
正在准备放入数据。。。
pool-1-thread-1 放入的数据 11
正在准备取数据。。。
pool-1-thread-4 放入的数据 26
正在准备取数据。。。
pool-1-thread-1 取到的数据为4
正在准备放入数据。。。
pool-1-thread-2 取到的数据为11
正在准备放入数据。。。
pool-1-thread-3 放入的数据 18
正在准备取数据。。。
...
...

LinkedBlockingQueue

LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出的原则对元素进行排序。

先看一下他的构造函数:

public LinkedBlockingQueue() {
this(Integer.MAX_VALUE); //MAX_VALUE=2147483647
} public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}

我们还是直接开看一个例子:

public class BlockingQueueTest {

    /**
* 定义装苹果的篮子
*/
public static class Basket {
// 篮子,能够容纳3个苹果
// BlockingQueue<String> basket = new ArrayBlockingQueue<String>(3);
BlockingQueue<String> basket = new LinkedBlockingQueue<String>(3); // 生产苹果,放入篮子
public void produce() throws InterruptedException {
// put方法放入一个苹果,若basket满了,等到basket有位置
basket.put("An apple");
} // 消费苹果,从篮子中取走
public String consume() throws InterruptedException {
// get方法取出一个苹果,若basket为空,等到basket有苹果为止
return basket.take();
}
} //  测试方法
public static void testBasket() { // 建立一个装苹果的篮子
final Basket basket = new Basket(); // 定义苹果生产者
class Producer implements Runnable {
public String instance = ""; public Producer(String a) {
instance = a;
} public void run() {
try {
while (true) {
// 生产苹果
System.out.println("生产者准备生产苹果:" + instance);
basket.produce();
System.out.println("! 生产者生产苹果完毕:" + instance);
// 休眠300ms
Thread.sleep(300);
}
} catch (InterruptedException ex) {
}
}
} // 定义苹果消费者
class Consumer implements Runnable {
public String instance = ""; public Consumer(String a) {
instance = a;
} public void run() {
try {
while (true) {
// 消费苹果
System.out.println("消费者准备消费苹果:" + instance);
basket.consume();
System.out.println("! 消费者消费苹果完毕:" + instance);
// 休眠1000ms
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
}
}
} ExecutorService service = Executors.newCachedThreadPool();
Producer producer = new Producer("P1");
Producer producer2 = new Producer("P2");
Consumer consumer = new Consumer("C1");
service.submit(producer);
service.submit(producer2);
service.submit(consumer); // 程序运行3s后,所有任务停止
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
} service.shutdownNow();
} public static void main(String[] args) {
BlockingQueueTest.testBasket();
}
}

输出为:

生产者准备生产苹果:P1
消费者准备消费苹果:C1
! 生产者生产苹果完毕:P1
生产者准备生产苹果:P2
! 消费者消费苹果完毕:C1
! 生产者生产苹果完毕:P2
生产者准备生产苹果:P2
! 生产者生产苹果完毕:P2
生产者准备生产苹果:P1
! 生产者生产苹果完毕:P1
生产者准备生产苹果:P2
生产者准备生产苹果:P1
消费者准备消费苹果:C1
! 消费者消费苹果完毕:C1
! 生产者生产苹果完毕:P2
生产者准备生产苹果:P2
消费者准备消费苹果:C1
! 消费者消费苹果完毕:C1
! 生产者生产苹果完毕:P1
生产者准备生产苹果:P1
消费者准备消费苹果:C1
! 消费者消费苹果完毕:C1
! 生产者生产苹果完毕:P2 Process finished with exit code 0

java并发编程(二十四)----(JUC集合)ArrayBlockingQueue和LinkedBlockingQueue介绍的更多相关文章

  1. java并发编程(十四)----(JUC原子类)对象的属性修改类型介绍

    今天我们介绍原子类的最后一个类型--对象的属性修改类型: AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUp ...

  2. Java并发编程(十四)Java内存模型

    1.共享内存和消息传递 线程之间的通信机制有两种:共享内存和消息传递:在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程 ...

  3. Java并发编程(十四)-- 线程池实现原理

    在上一章我们从宏观上介绍了ThreadPoolExecutor,本文将深入解析一下线程池的具体实现原理 原理解析 线程池状态 在ThreadPoolExecutor中定义了一个volatile变量,另 ...

  4. Java并发编程(十四)并发容器类

    同步容器将所有对容器状态的访问都串行化,以实现线程安全性.这种方法的代价是严重降低并发性,当多个线程竞争容器的锁时,吞吐量将严重减低. 另一个方面,并发容器是针对多个线程并发访问设计的.在java 5 ...

  5. java并发编程(十)----JUC原子类介绍

    今天我们来看一下JUC包中的原子类,所谓原子操作是指不会被线程调度机制打断的操作:这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程),原子操作可以是 ...

  6. java并发编程(十四)同步问题的内存可见性

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17288243 加锁(synchronized同步)的功能不仅仅局限于互斥行为,同时还存在另 ...

  7. java并发编程(十一)----(JUC原子类)基本类型介绍

    上一节我们说到了基本原子类的简单介绍,这一节我们先来看一下基本类型: AtomicInteger, AtomicLong, AtomicBoolean.AtomicInteger和AtomicLong ...

  8. Java并发编程二三事

    Java并发编程二三事 转自我的Github 近日重新翻了一下<Java Concurrency in Practice>故以此文记之. 我觉得Java的并发可以从下面三个点去理解: * ...

  9. java并发编程笔记(四)——安全发布对象

    java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...

随机推荐

  1. 使用OpenAPI构建更智能的API

    像OpenAPI这样的API描述规范是一个关键工具,您应该尽可能地将其好好掌握,记录和执行API的工作由计算机和开发人员完成:OpenAPI 3.0现在允许额外的表现力,可以让机器为我们做更多有用的工 ...

  2. Python线程池ThreadPoolExecutor源码分析

    在学习concurrent库时遇到了一些问题,后来搞清楚了,这里记录一下 先看个例子: import time from concurrent.futures import ThreadPoolExe ...

  3. Bzoj 2288 生日礼物题解

    2288: [POJ Challenge]生日礼物 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 856  Solved: 260[Submit][S ...

  4. java学习笔记(基础篇)--java关键字与数据类型

    java关键字与数据类型 Java语言的关键字是程序代码中的特殊字符.包括: . 类和接口的声明--class, extends, implements, interface . 包引入和包声明--i ...

  5. 7月18日刷题记录 二分答案跳石头游戏Getting

    通过数:1 明天就要暑假编程集训啦~莫名开心 今天做出了一道 二分答案题(好艰辛鸭) 1049: B13-二分-跳石头游戏(二分答案) 时间限制: 5 Sec  内存限制: 256 MB提交: 30  ...

  6. [原创]实现MongoDB数据库审计SQL语句的脚本

    功能:实现具体显示mongodb数据库表操作语句的状态和情况,使用awk和shell实现抓取和分析处理.脚本内容如下: #!/bin/bash if [ $# == 0 ];then echo &qu ...

  7. Contos7 常用命令

    ```pythoncentos常用命令: 查看所有运行的单元:systemctl list-units 查看所有单元:systemctl list-units --all 查看所有启动的服务:syst ...

  8. Ubuntu中目录右下角的锁标志

    在ubuntu中如果是用root权限创建的目录,在普通权限下展示时则会出现一个锁的标志,表明该目录是一个受保护的目录(只读,不能写入和删除),普通权限无法对其进行操作.如用命令sudo mkdir d ...

  9. MTSC2019第五届移动互联网测试开发大会PPT下载

    关注公众号「软件测试大本营」后台回复「MTSC」或「测试开发大会」即可获取云盘下载地址及提取码. 注:PPT文件版权归相关撰写人所有,仅供学习交流,请勿用于任何商业用途,谢谢 前不久在北京举办了第五届 ...

  10. Python学习1——Python中的 split() 函数

    函数:split() Python中有split()和os.path.split()两个函数,此处简单介绍split()函数:split():拆分字符串.通过指定分隔符对字符串进行切片,并返回分割后的 ...