Blocking Queue
使用队列,可以安全地从一个线程向另一个线程传递数据。
阻塞队列方法
方法 | 正常动作 | 特殊情况下的动作方法 |
add | 添加一个元素 | 如果队列满,则抛出IllegalStateException 异常 |
element | 返回队列的头元素 | 如果队列空,抛出NoSuchElementException异常 |
offer | 添加一个元素并返回true | 如果队列满,返回false |
peek | 返回队列的头元素 | 如果队列空,则返回null |
poll | 移出并返回队列的头元素 | 如果队列空,则返回null |
put | 添加一个元素 | 如果队列满,则阻塞 |
remove | 移出并返回头元素 | 如果队列空,则抛出NoSuchElementException异常 |
take | 移出并返回头元素 | 如果队列空,则阻塞 |
阻塞队列方法分为以下3种,取决于当队列满或者空时它们的响应方式。
- 如果将队列当作线程管理工具来使用,将要用到put和take方法。
- 向满的队列中添加或从空的队列移除元素时,add remove 和element操作抛出异常。
- 在一个多线程程序中,队列会在任何时候空或满,因此一定要使用offer poll和peek方法作为替代。这些方法如果不能完成任务,只是给出一个错误提示而不会抛出异常。
poll和peek方法返回空来指示失败。因此,向这些队列插入null值时非法的。
//在100ms内在队列的尾部插入一个元素
boolean success = q.offer(x,100,TimeUnit.MILLISECONDS);
不带超时参数时,offer和poll等效。
java.util.concurrent包提供了阻塞队列的几个变种。默认情况下,
LinkedBlockingQueue的容量是没有上边界的,但是,也可以选择指定最大容量。
LinkedBlockingDeque 是一个双端的版本。
ArrayBlockingQueue 在构造时需要指定容量,并且有一个可选的参数来指定是否需要公平性。若设置了公平参数,则那么等待了最长时间的线程会优先得到处理。通常,公平性会降低性能,只有在确实非常需要时才使用它。
PriorityBlockingQueue是一个带优先级的队列,而不是先进先出队列。元素按照它们的优先级顺序被移出。该队列是没有容量上限,但是,如果队列是空的,取元素的操作会阻塞。(有关优先级队列的详细内容参看第9章。)
最后,DelayQueue 包含实现Delayed接口的对象:interface Delayed extends Comparable<Delayed>{
long getDelay(TimeUnit unit);
}
getDelay方法返回对象的残留延迟。负值表示延迟已经结束。元素只有在延迟用完的情况下才能从DelayQueue移除。还必须实现compareTo方法。DelayQueue 使用该方法对元素进行排序。
Java sE 7增加了一个TransferQueue接口,允许生产者线程等待,直到消费者准备就绪可以接收一个元素。 如果生产者调用q. transfer(item);
这个调用会阻塞,直到另一个线程将元素(item)删除。LinkedTranserQueue类实现了这个接口。
以下程序展示了如何使用阻塞队列来控制一组线程。程序在一个目录及它的所有子目录下搜索所有文件,打印出包含指定关键字的行;
package com.example.demo.bean;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockQueueTest {
private static final int FILE_QUEUE_SIZE = 10;
private static final int SEARCH_THREADS = 100;
private static final File DUMMY = new File("");
//阻塞队列
private static BlockingQueue<File> queue = new ArrayBlockingQueue<>(FILE_QUEUE_SIZE);
private static int searchRowCount = 0;
public static void main(String[] args) {
try (Scanner in = new Scanner(System.in)) {
System.out.println("Enter base directory");
String directory = in.nextLine();
System.out.println("Enter keyword:");
String keyword = in.nextLine();
Runnable enumerator = () -> {
try {
enumerate(new File(directory));
queue.put(DUMMY);
} catch (InterruptedException e) {
}
};
new Thread(enumerator).start();
for (int i = 1; i < SEARCH_THREADS; i++) {
Runnable searcher = () -> {
try {
boolean done = false;
while (!done) {
File file = queue.take();
if (file == DUMMY) {
queue.put(file);
done = true;
} else {
search(file, keyword);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
}
};
new Thread(searcher).start();
}
}
}
public static void search(File file, String keyword) throws IOException{
try(Scanner in = new Scanner(file,"UTF-8")){
int lineNumber = 0;
while (in.hasNextLine()){
lineNumber++;
searchRowCount++;
String line = in.nextLine();
//System.out.println( "searchRowCount \t" + searchRowCount);
if(line.contains(keyword)){
System.out.printf("%s:%d:%s%n",file.getPath(),lineNumber,line);
}
}
}
}
public static void enumerate(File directory) throws InterruptedException{
File [] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory()){
enumerate(file);
}else {
queue.put(file);
}
}
}
}