现在,通过前几篇的总结,我们对Java多线程已经有所了解了,但是它们都是一些Java并发程序设计基础的底层构建块。对于实际编程来说,我们应该尽可能的远离底层结构。使用那些由并发处理的专业人士实现的较高层次的结构要方便的多,安全的多。

阻塞队列

对于许多线程问题。可以通过使用一个或多个队列以优雅且安全的方式将其形式化。生产者线程向队列插入元素,消费者线程则取出他们。使用队列,可以安全地从一个线程向另一个线程传递数据。

阻塞队列的方法

方法

正常动作

特殊情况下动作

add

添加一个元素

如果队列满,则抛出IllegalStateException异常

element

返回队列的头元素

如果队列空,抛出NoSuchElementException异常

offer

添加一个元素并返回true

如果队列满,返回false

peek

返回队列的头元素

如果队列空,则返回null

poll

移出并返回队列的头元素

如果队列空,则返回null

put

添加一个元素

如果队列满,则阻塞

remove

移出并返回头元素

如果队列空,则抛出NoSuchElementException异常

take

移出并返回头元素

如果队列满,则阻塞

阻塞队列的使用

接下来我们使用使用阻塞队列来控制一组线程。程序在一个目录及它的所有子目录下搜索所有文件,打印出包含指定关键字的行。

生产者线程列举所有子目录下的文件并把它们放在一个阻塞队列中。同时启动大量的搜索线程,每个搜索线程从队列中取出一个文件,打开它,打印所有包含关键字的行,然后去取下一个文件。终止线程方面,在队列最后添加一个虚拟对象,当搜索线程取到这个对象时,将它放回并终止线程。

FileEnumerationTask线程类:(将目录里的文件加入到队列)
/**
* @author xzzhao
*/
public class FileEnumerationTask implements Runnable {
public static File DUMMY = new File("");
private final BlockingQueue queue;
private final File startingDirectory;
public FileEnumerationTask(BlockingQueue queue, File startingDirectory) {
super();
this.queue = queue;
this.startingDirectory = startingDirectory;
}
@Override
public void run() {
try {
enumerate(startingDirectory);
queue.put(DUMMY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void enumerate(File directory) throws InterruptedException {
File[] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory()) {
enumerate(directory);
} else {
queue.put(file);
}
}
}
}
SearchTask线程类:(根据关键字搜索行)
/**
* @author xzzhao
*/
public class SearchTask implements Runnable {
private final BlockingQueue queue;
private final String keyword;
public SearchTask(BlockingQueue queue, String keyword) {
super();
this.queue = queue;
this.keyword = keyword;
}
@Override
public void run() {
try {
boolean done = false;
while (!done) {
File file = queue.take();
if (file == FileEnumerationTask.DUMMY) {
queue.put(file);
done = true;
} else {
search(file);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 根据关键字搜索
*
* @param file
* @throws FileNotFoundException
*/
public void search(File file) throws FileNotFoundException {
try (Scanner in = new Scanner(file)) {
int lineNumber = 0;
while (in.hasNextLine()) {
lineNumber++;
String line = in.nextLine();
if (line.contains(keyword)) {
System.out.println("路径:" + file.getPath() + " 行号:" + lineNumber + " 行:" + line);
}
}
}
}
}
BlockingQueueTest类:(测试)
/**
* @author xzzhao
*/
public class BlockingQueueTest {
public static void main(String[] args) {
final int FILE_QUEUE_SIZE = 10;
final int SERCH_THREADS = 100;
Scanner in = new Scanner(System.in);
System.out.println("请输入根目录 :");
String directory = in.nextLine();
System.out.println("请输入关键字 :");
String keyword = in.nextLine();
BlockingQueue queue = new ArrayBlockingQueue<>(FILE_QUEUE_SIZE);
FileEnumerationTask enumerator = new FileEnumerationTask(queue, new File(directory));
new Thread(enumerator).start();
for (int i = 0; i <= SERCH_THREADS; i++) {
new Thread(new SearchTask(queue, keyword)).start();
}
}
}

测试结果:

Java单线程任务队列 java 多线程 队列_搜索