今天看了下BlockingQueue的几种实现,记录下以便以后复习。

首先来看一下BlockingQueue的家族成员:

BlockingQueue除了先进先出外,还有两个操作:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

阻塞队列提供了四种处理方法:

方法\处理方式 抛出异常 返回特殊值 一直阻塞 超时退出
插入方法 add(e) offer(e) put(e) offer(e,time,unit)
移除方法 remove() poll() take() poll(time,unit)
检查方法 element() peek() 不可用 不可用

  • 抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出IllegalStateException(“Queue full”)异常。当队列为空时,从队列里获取元素时会抛出NoSuchElementException异常 。
  • 返回特殊值:插入方法会返回是否成功,成功则返回true。移除方法,则是从队列里拿出一个元素,如果没有则返回null
  • 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。
  • 超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。

ArrayBlockingQueue一个由数组支持的有界的阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。实现原理使用ReentrantLock和Condition。下边写个例子:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; /*
* 现有的程序代码模拟产生了16个日志对象,并且需要运行16秒才能打印完这些日志,
* 请在程序中增加4个线程去调用parseLog()方法来分头打印这16个日志对象,
* 程序只需要运行4秒即可打印完这些日志对象。
*/
public class PrintLogTest { public static void main(String[] args) throws Exception {
// 新建一个等待队列
final BlockingQueue<String> bq = new ArrayBlockingQueue<String>(16);
// 四个线程
for (int i = 0; i < 4; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (true ) {
try {
String log = (String) bq.take();
parseLog(log);
} catch (Exception e) {
}
}
}
}).start();
}
for (int i = 0; i < 16; i++) {
String log = (i + 1) + "–> ";
bq.put(log); // 将数据存到队列里!
}
}
// parseLog方法内部的代码不能改动
public static void parseLog(String log) {
System. out.println(log + System.currentTimeMillis());
try {
Thread. sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

LinkedBlockingQueue : 基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列 中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;只有当队列缓冲区达到最大值缓存容量时 (LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒,反 之对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别 采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

/** Lock held by take, poll, etc */

private final ReentrantLock takeLock = new ReentrantLock();

/** Lock held by put, offer, etc */

private final ReentrantLock putLock = new ReentrantLock();

PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定),但需要注意的是PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁。

              DelayQueue:是一个支持延时获取元素的使用优先级队列的实现的无界阻塞队列。队列中的元素必须实现Delayed接口和Comparable接口,也就是说DelayQueue里面的元素必须有public int compareTo( T o)和long getDelay(TimeUnit unit)方法存在,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。我们可以将DelayQueue运用在以下应用场景:
  • 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
  • 定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,从比如TimerQueue就是使用DelayQueue实现的。
(1)实现一个Student对象作为DelayQueue的元素必须实现Delayed 接口的两个方法;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class Student implements Delayed { //必须实现Delayed接口
       private String name ;
       private long submitTime ;// 交卷时间
       private long workTime ;// 考试时间
       public String getName() {
             return this .name + ” 交卷,用时” + workTime;
      }
       public Student(String name, long submitTime) {
             this.name = name;
             this.workTime = submitTime;
             this.submitTime = TimeUnit.NANOSECONDS.convert(submitTime, TimeUnit.MILLISECONDS ) + System.nanoTime ();
            System. out.println(this.name + ” 交卷,用时” + workTime);
      }
       //必须实现getDelay方法
       public long getDelay(TimeUnit unit) {
//          返回一个延迟时间
             return unit.convert(submitTime – System.nanoTime (), unit.NANOSECONDS );
      }
       //必须实现compareTo方法
       public int compareTo(Delayed o) {
//          比较的方法
            Student that = (Student) o;
             return submitTime > that.submitTime ? 1 : ( submitTime < that.submitTime ? -1 : 0);
      }
}
(2)执行运行类如下:
package demo.thread;
import java.util.concurrent.DelayQueue;
public class DelayQueueTest {
       public static void main(String[] args) throws Exception {
             // 新建一个等待队列
             final DelayQueue<Student> bq = new DelayQueue<Student>();
             for (int i = 0; i < 5; i++) {
                  Student student = new Student(“学生” +i,Math.round((Math. random()*10+i)));
                  bq.put(student); // 将数据存到队列里!
            }
             //获取但不移除此队列的头部;如果此队列为空,则返回 null。
            System. out.println(“bq.peek()”+bq.peek().getName());
             //获取并移除此队列的头部,在可从此队列获得到期延迟的元素,或者到达指定的等待时间之前一直等待(如有必要)。
             //poll(long timeout, TimeUnit unit) 大家可以试一试这个方法
      }
}

运行结果如下:每次运行结果都不一样,一问,我们获得永远是队列里面的第一个元素;

学生0 交卷,用时8
学生1 交卷,用时6
学生2 交卷,用时10
学生3 交卷,用时10
学生4 交卷,用时9
bq.peek()学生1 交卷,用时6

可以慢慢的在以后的工作当中体会DelayQueue的用法。

BlockingQueue队列学习的更多相关文章

  1. BlockingQueue<> 队列的作用

    BlockingQueue<> 队列的作用 BlockingQueue 实现主要用于生产者-使用者队列 BlockingQueue 实现主要用于生产者-使用者队列,BlockingQueu ...

  2. Java队列学习

    队列是Java集合中的重要组成部分,具有先进先出的特性,使其具有广泛的应用场景,比如排队等.因此今天就来学习一下Java中的队列.本文的例子使用的Java8环境. 继承类图 学习队列,首先要知道它的类 ...

  3. Java中的BlockingQueue队列

    BlockingQueue位于JDK5新增的concurrent包中,它很好地解决了多线程中,如何高效安全地“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极 ...

  4. BlockingQueue队列

    1.BlockingQueue定义的常用方法如下     抛出异常 特殊值 阻塞 超时 插入 add(e) offer(e) put(e) offer(e,time,unit) 移除 remove() ...

  5. PHP消息队列学习

    在我们平常网站设计时,会遇到“给用户群发短信”,“商城订单系统大批量订单处理”,“商城秒杀活动”等需求,这些功能,都有一个共同的特点:就是在面对高迸发的同时,必须要保证系统处理数据的有效性.那么如何处 ...

  6. MSMQ队列学习记录

    微软消息队列-MicroSoft Message Queue(MSMQ) 使用感受:简单. 一.windows安装MSMQ服务 控制面板->控制面板->所有控制面板项->程序和功能- ...

  7. RabbitMQ五种消息队列学习(三)–Work模式

    由于在实际应用中,简单队列模型无法解决很多实际问题,而且生产者和消费者是一对一的关系.模型较为单一.故引入Work模式. 结构图 一个生产者.多个消费者. 一个消息只能被一个消费者获取. 测试实现:  ...

  8. Rabbit五种消息队列学习(二) – 简单队列

    队列结构图 P:消息的生产者 C:消息的消费者 红色:队列 生产者将消息发送到队列,消费者从队列中获取消息. 测试 1.连接MQ public static Connection getConnect ...

  9. Rabbit五种消息队列学习(一) – 总述

    RabbitMQ支持五种消息传递类型,分别如下图所示: 上图中显示6中消息队列分别为: 1.简单队列 一个生产者将消息放到队列中,一个消费者监听队列 2.工作队列(Work queues) 一个生产者 ...

随机推荐

  1. Oracle11g安装完成后给用户解锁

    安装时候你可能忘记给普通用户scott解锁,导致安装成功后普通用户无法登录,那该怎么办呢? 先用system用户登录,登录成功之后就可以给其他用户解锁了. 如图: 同理,如果要锁定某一个用户,只需要把 ...

  2. a标签中的href="#"与href="javascript:void(0)"区别

    转自http://blog.csdn.net/fightplane/article/details/5190037 <a href="#"> 点击链接后,页面会向上滚到 ...

  3. 使用Cache防止多人同时修改同一条信息

    Default.aspx: <a href="Default2.aspx?id=123&type=11ad">打开第二个页面id=123</a>&l ...

  4. Maven浅析-2 什么是Maven

    1.简单点讲:Maven就是一个项目构建工具.它可以生成一个artifact(component),还可以帮我们管理项目依赖(如附加的组件Filters等). 2.从整体讲:Maven也可以看作一个项 ...

  5. [FTP] FTPHelper-FTP帮助类,常用操作方法 (转载)

    点击下载 FTPHelper.zip 这个类是FTP服务器的一些操作1.连接FTP服务器 2.上传3.下载4.删除文件5.获取当前目录下明细(包含文件和文件夹)  6.获取FTP文件列表(包括文件夹) ...

  6. Excel数据导入到oracle

    打开pl/sql,如图所示界面,点击菜单栏中的T00LS     ODBC Imtorper,打开ODBC Importer选项框       在Data fromODBC页中选择需要导入的文件的格式 ...

  7. ArcGIS JS Demo

    最近在用 ArcGIS 写了一个Demo 效果如下: 1. 引用 ArcGIS JS API arcgis_js_api/init.js 2. 引用 样式 arcgis_js_api/js/esri/ ...

  8. _BLOCK_TYPE_IS_VALID 问题解析及处理

    直接原因: 释放内存时,内存已经被修改或释放. 产生可能: 1.内存越界操作,踩了待释放指针头信息. 2.重复释放指针. 处理: 采用排除法,逐步屏蔽掉一些代码,当屏蔽某些代码时,不抛此异常,说明问题 ...

  9. COM简单应用示例

    使用com技术开发模式进行的示例. com技术关键部分源码:主要将所有接口都写入到这个文件中 testinterface.h #ifndef TESTINTERFACE_H #define TESTI ...

  10. 【elasticsearch】(4)centos7 超简单安装elasticsearch 的 jdbc

    前言 elasticsearch(下面简称ES)使用jdbc连接mysql比go-mysql-elasticsearch的elasticsearch-river-jdbc能够很好的支持增量数据更新的问 ...