`

java阻塞队列(线程同步合作)

阅读更多

Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法了,而不能直接访问 LinkedList的非Queue的方法),以使得只有恰当的方法才可以使用。BlockingQueue 继承了Queue接口。

  队列是一种数据结构.它有两个基本操作:在队列尾部加人一个元素,和从队列头部移除一个元素就是说,队列以一种先进先出的方式管理数据,如果你试图向一个已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.在多线程进行合作时,阻塞队列是很有用的工具。工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线线程把中间结果取出并在将来修改它们。队列会自动平衡负载。如果第一个线程集运行得比第二个慢,则第二个线程集在等待结果时就会阻塞。如果第一个线程集运行得快,那么它将等待第二个线程集赶上来。下表显示了jdk1.5中的阻塞队列的操作:

  add        增加一个元索                     如果队列已满,则抛出一个IIIegaISlabEepeplian异常

  remove   移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常

  element  返回队列头部的元素             如果队列为空,则抛出一个NoSuchElementException异常

  offer       添加一个元素并返回true       如果队列已满,则返回false

  poll         移除并返问队列头部的元素    如果队列为空,则返回null

  peek       返回队列头部的元素             如果队列为空,则返回null

  put         添加一个元素                      如果队列满,则阻塞

  take        移除并返回队列头部的元素     如果队列为空,则阻塞

  remove、element、offer 、poll、peek 其实是属于Queue接口。

  阻塞队列的操作可以根据它们的响应方式分为以下三类:aad、removee和element操作在你试图为一个已满的队列增加元素或从空队列取得元素时抛出异常。当然,在多线程程序中,队列在任何时间都可能变成满的或空的,所以你可能想使用offer、poll、peek方法。这些方法在无法完成任务时只是给出一个出错示而不会抛出异常。

  注意:poll和peek方法出错进返回null。因此,向队列中插入null值是不合法的。

  还有带超时的offer和poll方法变种,例如,下面的调用:

  boolean success = q.offer(x,100,TimeUnit.MILLISECONDS);

  尝试在100毫秒内向队列尾部插入一个元素。如果成功,立即返回true;否则,当到达超时进,返回false。同样地,调用:

  Object head = q.poll(100, TimeUnit.MILLISECONDS);

  如果在100毫秒内成功地移除了队列头元素,则立即返回头元素;否则在到达超时时,返回null。

  最后,我们有阻塞操作put和take。put方法在队列满时阻塞,take方法在队列空时阻塞。

  java.ulil.concurrent包提供了阻塞队列的4个变种。默认情况下,LinkedBlockingQueue的容量是没有上限的(说的不准确,在不指定时容量为Integer.MAX_VALUE,不要然的话在put时怎么会受阻呢),但是也可以选择指定其最大容量,它是基于链表的队列,此队列按 FIFO(先进先出)排序元素。

  ArrayBlockingQueue在构造时需要指定容量,并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来达到这种公平性的:即等待时间最长的线程会先操作)。通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队列,此队列按 FIFO(先进先出)原则对元素进行排序。

  PriorityBlockingQueue是一个带优先级的队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限(看了一下源码,PriorityBlockingQueue是对PriorityQueue的再次包装,是基于堆数据结构的,而PriorityQueue是没有容量限制的,与ArrayList一样,所以在优先阻塞队列上put时是不会受阻的。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError),但是如果队列为空,那么取元素的操作take就会阻塞,所以它的检索操作take是受阻的。另外,往入该队列中的元素要具有比较能力。

  最后,DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。 下面是延迟接口:

  Java代码

  public interface Delayed extends Comparable<Delayed> {

  long getDelay(TimeUnit unit);

  }

  public interface Delayed extends Comparable<Delayed> {

  long getDelay(TimeUnit unit);

  }

  放入DelayQueue的元素还将要实现compareTo方法,DelayQueue使用这个来为元素排序。

  下面的实例展示了如何使用阻塞队列来控制线程集。程序在一个目录及它的所有子目录下搜索所有文件,打印出包含指定关键字的文件列表。从下面实例可以看出,使用阻塞队列两个显着的好处就是:多线程操作共同的队列时不需要额外的同步,另外就是队列会自动平衡负载,即那边(生产与消费两边)处理快了就会被阻塞掉,从而减少两边的处理速度差距。下面是具体实现:

  Java代码

  public class BlockingQueueTest {

  public static void main(String[] args) {

  Scanner in = new Scanner(System.in);

  System.out.print("Enter base directory (e.g. /usr/local/jdk5.0/src): ");

  String directory = in.nextLine();

  System.out.print("Enter keyword (e.g. volatile): ");

  String keyword = in.nextLine();

  final int FILE_QUEUE_SIZE = 10;// 阻塞队列大小

  final int SEARCH_THREADS = 100;// 关键字搜索线程个数

  // 基于ArrayBlockingQueue的阻塞队列

  BlockingQueue<File> queue = new ArrayBlockingQueue<File>(

  FILE_QUEUE_SIZE);

  //只启动一个线程来搜索目录

  FileEnumerationTask enumerator = new FileEnumerationTask(queue,

  new File(directory));

  new Thread(enumerator).start();

  //启动100个线程用来在文件中搜索指定的关键字

  for (int i = 1; i <= SEARCH_THREADS; i++)

  new Thread(new SearchTask(queue, keyword)).start();

  }

  }

  class FileEnumerationTask implements Runnable {

  //哑元文件对象,放在阻塞队列最后,用来标示文件已被遍历完

  public static File DUMMY = new File("");

  private BlockingQueue<File> queue;

  private File startingDirectory;

  public FileEnumerationTask(BlockingQueue<File> queue, File startingDirectory) {

  this.queue = queue;

  this.startingDirectory = startingDirectory;

  }

 

Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法了,而不能直接访问 LinkedList的非Queue的方法),以使得只有恰当的方法才可以使用。BlockingQueue 继承了Queue接口。

  队列是一种数据结构.它有两个基本操作:在队列尾部加人一个元素,和从队列头部移除一个元素就是说,队列以一种先进先出的方式管理数据,如果你试图向一个已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.在多线程进行合作时,阻塞队列是很有用的工具。工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线线程把中间结果取出并在将来修改它们。队列会自动平衡负载。如果第一个线程集运行得比第二个慢,则第二个线程集在等待结果时就会阻塞。如果第一个线程集运行得快,那么它将等待第二个线程集赶上来。下表显示了jdk1.5中的阻塞队列的操作:

  add        增加一个元索                     如果队列已满,则抛出一个IIIegaISlabEepeplian异常

  remove   移除并返回队列头部的元素    如果队列为空,则抛出一个NoSuchElementException异常

  element  返回队列头部的元素             如果队列为空,则抛出一个NoSuchElementException异常

  offer       添加一个元素并返回true       如果队列已满,则返回false

  poll         移除并返问队列头部的元素    如果队列为空,则返回null

  peek       返回队列头部的元素             如果队列为空,则返回null

  put         添加一个元素                      如果队列满,则阻塞

  take        移除并返回队列头部的元素     如果队列为空,则阻塞

  remove、element、offer 、poll、peek 其实是属于Queue接口。

  阻塞队列的操作可以根据它们的响应方式分为以下三类:aad、removee和element操作在你试图为一个已满的队列增加元素或从空队列取得元素时抛出异常。当然,在多线程程序中,队列在任何时间都可能变成满的或空的,所以你可能想使用offer、poll、peek方法。这些方法在无法完成任务时只是给出一个出错示而不会抛出异常。

  注意:poll和peek方法出错进返回null。因此,向队列中插入null值是不合法的。

  还有带超时的offer和poll方法变种,例如,下面的调用:

  boolean success = q.offer(x,100,TimeUnit.MILLISECONDS);

  尝试在100毫秒内向队列尾部插入一个元素。如果成功,立即返回true;否则,当到达超时进,返回false。同样地,调用:

  Object head = q.poll(100, TimeUnit.MILLISECONDS);

  如果在100毫秒内成功地移除了队列头元素,则立即返回头元素;否则在到达超时时,返回null。

  最后,我们有阻塞操作put和take。put方法在队列满时阻塞,take方法在队列空时阻塞。

  java.ulil.concurrent包提供了阻塞队列的4个变种。默认情况下,LinkedBlockingQueue的容量是没有上限的(说的不准确,在不指定时容量为Integer.MAX_VALUE,不要然的话在put时怎么会受阻呢),但是也可以选择指定其最大容量,它是基于链表的队列,此队列按 FIFO(先进先出)排序元素。

  ArrayBlockingQueue在构造时需要指定容量,并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来达到这种公平性的:即等待时间最长的线程会先操作)。通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队列,此队列按 FIFO(先进先出)原则对元素进行排序。

  PriorityBlockingQueue是一个带优先级的队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限(看了一下源码,PriorityBlockingQueue是对PriorityQueue的再次包装,是基于堆数据结构的,而PriorityQueue是没有容量限制的,与ArrayList一样,所以在优先阻塞队列上put时是不会受阻的。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError),但是如果队列为空,那么取元素的操作take就会阻塞,所以它的检索操作take是受阻的。另外,往入该队列中的元素要具有比较能力。

  最后,DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。 下面是延迟接口:

  Java代码

  public interface Delayed extends Comparable<Delayed> {

  long getDelay(TimeUnit unit);

  }

  public interface Delayed extends Comparable<Delayed> {

  long getDelay(TimeUnit unit);

  }

  放入DelayQueue的元素还将要实现compareTo方法,DelayQueue使用这个来为元素排序。

  下面的实例展示了如何使用阻塞队列来控制线程集。程序在一个目录及它的所有子目录下搜索所有文件,打印出包含指定关键字的文件列表。从下面实例可以看出,使用阻塞队列两个显着的好处就是:多线程操作共同的队列时不需要额外的同步,另外就是队列会自动平衡负载,即那边(生产与消费两边)处理快了就会被阻塞掉,从而减少两边的处理速度差距。下面是具体实现:

  Java代码

  public class BlockingQueueTest {

  public static void main(String[] args) {

  Scanner in = new Scanner(System.in);

  System.out.print("Enter base directory (e.g. /usr/local/jdk5.0/src): ");

  String directory = in.nextLine();

  System.out.print("Enter keyword (e.g. volatile): ");

  String keyword = in.nextLine();

  final int FILE_QUEUE_SIZE = 10;// 阻塞队列大小

  final int SEARCH_THREADS = 100;// 关键字搜索线程个数

  // 基于ArrayBlockingQueue的阻塞队列

  BlockingQueue<File> queue = new ArrayBlockingQueue<File>(

  FILE_QUEUE_SIZE);

  //只启动一个线程来搜索目录

  FileEnumerationTask enumerator = new FileEnumerationTask(queue,

  new File(directory));

  new Thread(enumerator).start();

  //启动100个线程用来在文件中搜索指定的关键字

  for (int i = 1; i <= SEARCH_THREADS; i++)

  new Thread(new SearchTask(queue, keyword)).start();

  }

  }

  class FileEnumerationTask implements Runnable {

  //哑元文件对象,放在阻塞队列最后,用来标示文件已被遍历完

  public static File DUMMY = new File("");

  private BlockingQueue<File> queue;

  private File startingDirectory;

  public FileEnumerationTask(BlockingQueue<File> queue, File startingDirectory) {

  this.queue = queue;

  this.startingDirectory = startingDirectory;

  }

分享到:
评论

相关推荐

    java多线程编程总结

    详细的讲述了多线程的各种用法 Java线程:概念与原理 ...Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器 Java线程:大总结

    Java多线程编程总结

    Java 线程系列博文总结word化,编目如下,欢迎互相学习交流: ...Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器

    Java 线程总结

    Java线程:概念与原理 Java线程:创建与启动 ...Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器 Java线程:大总结

    java线程详解

    八、线程同步小结 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:volatile关键字 Java线程:新特征-线程池 一、固定大小的线程池 二、单任务线程池 三、可变尺寸的线程池 四、延迟连接池 五、...

    Java并发编程:阻塞队列

     使用非阻塞队列的时候有一个很大问题是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,必须额外地实现同步策略以及线程间唤醒策略,这个实现起来非常麻烦。但是有了阻塞队列不一样了,它会

    并发编程实践,全面介绍基础知识、JVM同步原语、线程安全、低级并发工具、线程安全容器、高级线程协作工具、Executor部分等

    详细介绍java并发编程相关知识: ...  阻塞队列 高级线程协作工具   信号量   闭锁   关卡 fork-join Executor部分   Executor基础 ThreadPoolExecutor的定制   线程的中断与任务的取消   其他

    详解Java编程中线程同步以及定时启动线程的方法

    主要介绍了详解Java编程中线程同步以及定时启动线程的方法, 讲到了wait()与notify()方法以及阻塞队列等知识,需要的朋友可以参考下

    个人总结的深入java多线程开发

    看完《think in java》多线程章节,自己写的多线程文档,还结合了其他的相关网络资料。 线程 一. 线程池 1)为什么要使用线程池 2 2)一个具有线程池的工作队列 3 3)使用线程池的风险: 4 4)有效使用线程池的原则 5...

    线程安全队列Queue

    在项目启动时,开一个单线程来专门处理巡检任务的下发给巡检服务组件。使用BlockingQueue阻塞算法。BlockingQueue作为线程容器,可以为线程同步提供有力的保障。

    Java并发编程相关源码集 包括多任务线程,线程池等.rar

    多个线程竞争问题、多个线程多个锁问题、创建一个缓存的线程池、多线程使用Vector或者HashTable的示例(简单线程同步问题)、PriorityBlockingQueue示例、高性能无阻塞无界队列: ConcurrentLinkedQueue、DelayQueue...

    UDP+阻塞队列+多线程

    练习 Java 多线程编程技术。 练习实现网络并发服务的编程技术。 学习如何实现多线程间的相互同步和相互协作。 理解什么是线程安全。 二. 设计要求 功能概述:实现一个支持并发服务的网络运算服务器程序。该服务器...

    2万字Java并发编程面试题合集(含答案,建议收藏)

    1、在 java 中守护线程和本地线程区别?2、线程与进程的区别?3、什么是多线程中的上下文切换?4、死锁与活锁的区别,死锁与饥饿的区别?5、Java 中用到的线程调度算法是什么?6、什么是线程组,为什么在 Java 中不...

    Java 7并发编程实战手册

    全书分为9章,涵盖了线程管理、线程同步、线程执行器、Fork/Join框架、并发集合、定制并发类、测试并发应用等内容。全书通过60多个简单而非常有效的实例,帮助读者快速掌握Java 7多线程应用程序的开发技术。学习完...

    java并发库高级应用源码--张孝祥

    java并发库thread使用,传统线程技术、定时器技术、线程互斥技术,同步通讯技术、多线程共享数据、并发库应用,线程锁技术,阻塞锁、阻塞队列,线程池等应用

    Java并发编程:同步容器

    为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器、并发容器、阻塞队列、Synchronizer(比如CountDownLatch)。我们来讨论下同步容器。  一.为什么会出现同步容器?  在...

    Java并发编程实战

    5.3 阻塞队列和生产者-消费者模式 5.3.1 示例:桌面搜索 5.3.2 串行线程封闭 5.3.3 双端队列与工作密取 5.4 阻塞方法与中断方法 5.5 同步工具类 5.5.1 闭锁 5.5.2 FutureTask 5.5.3 信号量 5.5.4 栅栏 5.6...

    java语言操作系统课程设计模拟进程管理系统源码.zip

    ③阻塞、唤醒:利用系统提供的线程类模仿进程,直接启动相关的系统调用即可使线程暂停或恢复,从而达到阻塞和唤醒的目的; 针对需求3 :通过编写P、V原语来实现进程的同步; 针对需求4:为进程管理器类编写调度函数...

    Java并发编程实践 PDF 高清版

    5.3 阻塞队列和生产者一消费者模式 5.4 阻塞和可中断的方法 5.5 Synchronizer 5.6 为计算结果建立高效、可伸缩的高速缓存 第2部分 构建并发应用程序 第6章 任务执行 6.1 在线程中执行任务 6.2 Executor 框架 6.3 ...

Global site tag (gtag.js) - Google Analytics