`

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和C++等面向对象语言中扮演着重要角色。标题中的“支持多线程和泛型的阻塞队列”意味着我们讨论的是一个能够同时处理多...

    JAVA实现线程间同步与互斥生产者消费者问题

    在Java编程中,线程同步和互斥是多线程编程中的重要概念,它们用于解决多个线程同时访问共享资源时可能出现的问题。本项目通过一个生产者消费者问题的实例,展示了如何在Java中实现线程间的同步与互斥。 生产者消费...

    java线程聊天室(阻塞队列实现)

    而阻塞队列(Blocking Queue)是Java并发包(java.util.concurrent)中的一种高效数据结构,常用于线程间的协作,它能够简化同步问题并提高系统性能。 阻塞队列是一种特殊的队列,当队列为空时,取出元素的操作将会...

    Java多线程编程总结

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

    操作系统实验 多线程同步与互斥 java编写 有界面

    在“操作系统实验 多线程同步与互斥 java编写 有界面”的实验中,可能需要设计一个图形用户界面(GUI),通过按钮或事件触发线程的创建和同步操作,直观地展示线程间的交互和同步效果。例如,可以模拟银行账户转账,...

    java阻塞队列实现原理及实例解析.docx

    总结来说,Java阻塞队列是一种强大的并发工具,它通过自动阻塞和唤醒线程来实现线程间的同步,简化了多线程编程的复杂性。在Java `java.util.concurrent`包中提供了多种优化的阻塞队列实现,适用于不同的并发需求。...

    java线程同步及通信

    Java线程同步与通信是多线程编程中的关键概念,用于解决并发访问共享资源时可能出现的数据不一致性和竞态条件问题。以下将详细介绍这两个主题,以及如何通过代码示例进行演示。 1. **线程同步**: 线程同步是确保...

    java多线程经典案例

    本案例将深入探讨Java多线程中的关键知识点,包括线程同步、线程通信和线程阻塞。 线程同步是为了防止多个线程同时访问共享资源,导致数据不一致。Java提供了多种同步机制,如synchronized关键字、Lock接口...

    Java实现简单的阻塞队列2种方式

    在Java编程中,阻塞队列是一种特殊类型的并发数据结构,它在多线程环境中的应用广泛,主要用于线程间的协作通信。阻塞队列在队列满时会阻止生产者线程添加元素,在队列空时会阻止消费者线程取出元素,直到条件满足...

    java线程同步

    Java线程同步是多线程编程中的一个重要概念,它用于解决在并发环境下多个线程对共享资源的访问问题,以防止数据的不一致性。在Java中,线程同步的机制主要包括锁、同步块、同步方法、volatile关键字以及线程通信(如...

    java阻塞队列实现原理及实例解析

    Java阻塞队列是一种特殊的队列,它能够在队列为空或满时阻塞线程,使得线程之间能够更好地协作和通信。阻塞队列的实现原理是基于锁机制和条件变量机制的,通过wait和notify方法来实现线程之间的同步。 阻塞队列与...

    java学习(基于Java阻塞队列的搜索实例).pdf

    通过以上分析,我们了解到基于Java阻塞队列的搜索实例利用了Java并发API的强大功能来实现多线程间的高效协作,从而完成复杂的文件搜索任务。在多线程编程实践中,理解阻塞队列的工作原理和正确使用它们是关键的一步...

    java+多线程+同步详解源码整理

    3. **线程同步** 在多线程环境中,数据共享可能导致数据不一致,为了解决这个问题,Java提供了多种同步机制。包括: - **synchronized关键字**:用于方法或代码块,确保同一时间只有一个线程访问特定资源。 - **...

    消息分发框架(基于JAVA阻塞队列实现、 生产者消费者模型)

    然而,过度的线程创建会导致上下文切换开销,因此需要权衡线程池大小、线程优先级和线程同步策略。Java的`ExecutorService`和`ThreadPoolExecutor`提供了一种管理线程池的优雅方式,可以控制线程的生命周期和任务...

    Java中使用阻塞队列控制线程集实例

    在Java编程中,阻塞队列(BlockingQueue)是一种重要的并发工具,用于在多个线程之间传递和协调任务。在上述实例中,阻塞队列被用来控制线程集,以便在目录及其子目录中搜索包含特定关键字的文件。这个程序涉及到几...

    Java线程同步例子.pdf

    在Java中,线程同步是保证多线程安全访问共享资源的重要机制。多个线程在运行过程中,访问共享数据或者对共享数据进行修改,必须采用线程同步技术,以避免出现数据不一致的问题。根据提供的文件内容,我们可以看到一...

    java多线程Demo

    3. 多线程同步与通信: 在多线程环境下,可能会出现数据竞争问题,为了解决这个问题,Java提供了多种同步机制,如synchronized关键字、wait/notify机制、Lock锁(ReentrantLock)等。synchronized用于控制对共享...

    Java多线程示例之线程控制

    总之,Java多线程技术是软件开发中的重要技能,它涉及到线程池的使用、线程同步和通信等多个方面。通过学习和理解`MaxThreadCountTest`中的例子,开发者可以更好地掌握如何在实际项目中控制线程数量,优化程序性能,...

Global site tag (gtag.js) - Google Analytics