首页 > 代码库 > CoreJava_线程并发(阻塞队列):在某个文件夹下搜索含有某关键字的文件

CoreJava_线程并发(阻塞队列):在某个文件夹下搜索含有某关键字的文件

    Java多线程编程是非常考验一个程序员水平的。传统的WEB程序中,因为框架提供了太多的健壮性、并发性、可靠性的支持,所以我们都是将所有的注意力放到了业务实现上。我们只是按照业务逻辑的要求,不停的积累自己的代码。因为知识,或者是经验的限制,经常出现了问题而不自知。例如,某些比较原始的项目中,并没有使用Spring等相对来说比较灵活健壮的框架。而是仅仅使用Servlet来作为服务端的实现方式。


    举一个简单的栗子,众所周知,当请求到了容器,容器是创建并且启动了一个Servlet线程来对当前的请求作出相应,这个时候,Servlet中的成员变量就会受到多线程的影响,这样,在Servlet中书写成员变量代码就会变得非常的危险,因为毕竟不是一个线程安全的设计。多线程的同步、互斥、竞争、配合以及线程的中断等成了我们在编码过程中,非常注意的一个知识点。


    Java核心技术中,说到如何对多线程进行并发控制:首先:建议使用阻塞队列,阻塞队列是由专业的线程安全的一个数据结构,我们在这个上面进行编程,就如同站在巨人的肩膀上,至于很多低层的实现就是他们去考虑的事情了。其次,如果想自己控制多线程并发的时候,尽可能的推荐使用synchronized关键字,synchronized关键字实际上就是等同于ReentrantLock或者读写锁的实现,不过语法更加简洁,更不太容易出错。FINALLY,最后,如果有充足的理由的时候,才会推荐使用ReentrantLock或者ReentrantReadWriteLock、ReadLock、WriteLock以及其相应的condition条、volatile等等,因为这些太过于低层,而且按照java一贯的实现编码逻辑,我们只需要知道某一个控制线程并发数据结构的存在就可以了。


    下面是在核心技术上的一个非常好的,可以用来研究阻塞队列的Demo,目的是用于在某一个给定的文件夹下面,搜索所有含有某个关键字的文件,这中功能在很多编辑器下都有具体的实现,例如UE。


    功能的实现方式大概是这样的:生产者线程,将当前给定的文件夹下面的所有的文件,放到阻塞队列中(功能实现中使用了递归),最后,放置一个空的文件,作为一个标志,类似于一个信号灯的样子。创建了非常多的消费者线程,从阻塞队列中,循环获取一个文件,然后进行逐行遍历,如果当中含有关键字则打印,跳出循环的条件是看到了消费者线程放置的信号灯。功能的实现将并发依赖于一个并发的数据结构,很值得去借鉴。

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;


public class BlockingQueueTest {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		System.out.println("Enter base directory(eg. /usr/local/jdk1.6.0/src)");
		String directory = in.nextLine();
		System.out.println("Enter keyword(e.g. volatile):");
		String keyword = in.nextLine();
		
		final int FILE_QUEUE_SIZE=10;
		final int SEARCH_THREADS =100;
		
		BlockingQueue<File> queue = new ArrayBlockingQueue<File>(FILE_QUEUE_SIZE);
		
		FileEnumerationTask enumerator = new FileEnumerationTask(queue,new File(directory));
		new Thread(enumerator).start();
		for(int i=1;i<=SEARCH_THREADS;i++){
			new Thread(new SearchTask(queue,keyword)).start();
		}
	}
}

class FileEnumerationTask implements Runnable{

	public FileEnumerationTask(BlockingQueue<File> queue,File startingDirectory){
		this.queue=queue;
		this.startingDirectory = startingDirectory;
	}
	@Override
	public void run() {
		try{
			enumerate(startingDirectory);
			queue.put(DUMMY);
		}catch(InterruptedException e){
		}
	}
	public 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 File DUMMY = new File("");
	
	private BlockingQueue<File> queue;
	private File startingDirectory;
}

class SearchTask implements Runnable{
	public SearchTask(BlockingQueue<File> queue,String keyword){
		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){
			
		} 
		catch (InterruptedException e) {
		}
	}
	
	private void search(File file)throws IOException{
		Scanner in = new Scanner(new FileInputStream(file));
		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);
		}
		in.close();
	}

	private BlockingQueue<File> queue;
	private String keyword;
}