阻塞队列笔记
1.阻塞队列的应用场景
我们来看这个场景,监控问及那变化的时候,我们开一个线程获取变化了的文件名,拿到之后我们会做解析,查找数据等等其它操作。如果我们对这两个操作分开统计他们的处理时间,会发现后面的解析时间远远大于前面得到的文件名的时间。
这时如果监控文件变化的线程是每秒轮询一次,假设操作1耗时1ms,操作2耗时正好与操作1加起来在一秒内,有事由于文件过大,解析时间过长超出一秒,就会出现有文件被监控到发生了变化去来不及处理,所以这批文件变化被漏掉了。
把上面这个场景抽象化,就是在生产者消费者模型中,生产数据和消费数据的速率不一样,如果生产速度快一些,消费处理不过来,就会导致数据丢失,这时候我们就应该用阻塞队列来解决这个问题。
阻塞队列首先是一个队列,我们起单线程生产数据入队,起多线程消费数据。由于阻塞队列的特点:队空是消费阻塞,队满时生产阻塞。多线程消费数据起到了加速消费的作用,使得生产的数据不会再队里积压过多,而生产的数据也不会丢失处理了。
2.阻塞队列 集合类
JDK7提供了7个阻塞队列。分别是
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue:一个使用优先级队列实现的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronouwQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
3.举个栗子
下面的实例展示了如何使用阻塞队列来控制线程集。程序在一个目录及它的所有子目录下搜索所有文件,打印出包含指定关键字的文件列表。从下面实例可以看出,使用阻塞队列两个显着的好处就是:多线程操作共同的队列时不需要额外的同步,另外就是队列会自动平衡负载,即那边(生产与消费两边)处理快了就会被阻塞掉,从而减少两边的处理速度差距。
下面是具体实现:
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 queue = new ArrayBlockingQueue(
FILE_QUEUE_SIZE);
//只启动一个线程来搜索目录
FileEnumerationTask enumerator = new FileEnumerationTask(queue,
new File(directory));
new Thread(enumerator)。
start();
//启动100个线程用来在文件中搜索指定的关键字
for (int i = 1; i queue;
private File startingDirectory;
public FileEnumerationTask(BlockingQueue queue, File startingDirectory) {
this。
queue = queue;
this。startingDirectory = startingDirectory;
}。