对于许多线程问题, 可以通过使用一个或多个队列以优雅且安全的方式将其形式化。生产者线程向队列插人元素, 消费者线程则取出它们。 使用队列, 可以安全地从一个线程向另 一个线程传递数据。
在使用队列的情况下,不需要同步,无需考虑线程之间的安全问题。(当然, 线程安全的队列类的实现者不能不考虑锁和条 件, 但是, 那是他们的问题而不是你的问题。)
当试图向队列添加元素而队列已满, 或是想从队列移出元素而队列为空的时候, 阻塞队 列(blocking queue ) 导致线程阻塞。 在协调多个线程之间的合作时, 阻塞队列是一个有用的 工具。 工作者线程可以周期性地将中间结果存储在阻塞队列中。 其他的工作者线程移出中间 结果并进一步加以修改。 队列会自动地平衡负载。 如果第一个线程集运行得比第二个慢, 第 二个线程集在等待结果时会阻塞。 如果第一个线程集运行得快, 它将等待第二个队列集赶上 来。
在JDK中,java.util.concurrent.BlockingQueue接口定义了阻塞队列的方法:
阻塞队列方法分为以下 3 类, 这取决于当队列满或空时它们的响应方式。
- 如果将队列当 作线程管理工具来使用, 将要用到 put 和 take 方法。
- 当试图向满的队列中添加或从空的队列 中移出元素时, add、 remove 和 element 操作抛出异常。
- 当然, 在一个多线程程序中, 队列会 在任何时候空或满, 因此, 一定要使用 offer、 poll 和 peek 方法作为替代。 这些方法如果不能 完成任务, 只是给出一个错误提示而不会抛出异常。
- 警告:poll 和 peek 方法返回空来指示失败。 因此, 向这些队列中插入 null 值是非法的。
java.util.concurrent 包提供了阻塞队列的几个变种。默认情况下,** LinkedBlockingQueue **的容量是没有上边界的, 但是, 也可以选择指定最大容量。 LinkedBlockingDeque 是一个双端 的版本。ArrayBlockingQueue 在构造时需要指定容量, 并且有一个可选的参数来指定是否需 要公平性。若设置了公平参数, 则那么等待了最长时间的线程会优先得到处理。通常, 公平 性会降低性能, 只有在确实非常需要时才使用它。PriorityBlockingQueue 是一个带优先级的队列, 而不是先进先出队列。元素按照它们的 优先级顺序被移出。 该队列是没有容量上限, 但是, 如果队列是空的, 取元素的操作会阻塞。**DelayQueue **包含实现 Delayed 接口的对象.getDelay 方法返回对象的残留延迟。 负值表示延迟已经结束。 元素只有在延迟用完的情 况下才能从 DelayQueue 移除。 还必须实现 compareTo 方法。 DelayQueue 使用该方法对元素 进行排序。
JDK7 增加了一个 TranSferQueUe 接口, 允许生产者线程等待, 直到消费者准备就绪 可以接收一个元素。 如果生产者调用 q.transfer(iteni) ;这个调用会阻塞, 直到另一个线程将元素 ( item ) 删除。 LinkedTransferQueue 类实现了这个接口。