java.util.concurrent包中的BlockingQueue可以安全地从一个线程向另一个线程传递数据。生产者线程向队列插入元素,消费者线程则取出它们。
当试图向队列添加元素而队列满,或想从队列移出元素而队列空,阻塞队列导致线程阻塞。
阻塞队列方法
方法 | 正常动作 | 特殊情况下动作 |
---|---|---|
put | 添加一个元素 | 若队列满,则阻塞 |
take | 移出并返回头元素 | 若队列空,则阻塞 |
offer | 添加一个元素并返回true | 若队列满,则返回false |
poll | 移出并返回头元素 | 若队列空,则返回null |
peek | 返回头元素 | 若队列空,则返回null |
add | 添加一个元素 | 若队列满,则抛出IllegalStateException异常 |
remove | 移出并返回头元素 | 若队列空,则抛出NoSuchElementException异常 |
element | 返回头元素 | 若队列空,则抛出NoSuchElementException异常 |
- 将队列当作线程管理工具,对应使用put和take方法
- 在一个多线程程序中,队列会在任何时候空或满,一定要使用offer、poll和peek方法,因为add、remove和element会抛出异常。
- poll和peek返回空来指示失败,所以队列不允许插入null
- ArrayBlockingQueue构造时需要指定容量,并有一个可选参数来指定是否需要公平性。若设置公平参数,等待最长时间的线程会优先得到处理。公平性通常会降低性能。
- LinkedBlockingQueue容量没有上界,也可以指定最大容量
- LinkedBlockingDueue双端队列
- PriorityBlockingQueue优先级队列,按照优先级顺序被移出
- DelayQueue当延迟时间结束时才能移出元素
使用阻塞队列控制一组线程。程序在一个目录及它所有子目录下搜索所有文件,打印包含指定关键字的行。
public class BlockingQueueTest {
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);
public static void main(String[] args) {
try (Scanner in = new Scanner(System.in)) {
System.out.print("Enter base directory (e.g. /opt/jdk1.8.0/src): ");
String directory = in.nextLine();
System.out.print("Enter keyword (e.g. volatile): ");
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 enumerate(File directory) throws InterruptedException {
File[] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory())
enumerate(file);
else
queue.put(file);
}
}
// 搜索指定文件中的关键字并打印所在行
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++;
String line = in.nextLine();
if (line.contains(keyword)) {
System.out.printf("%s:%d:%s%n", file.getPath(), lineNumber, line);
}
}
}
}
}