一个简单高效的多线程解决方案

import java.io.File;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 多线程抓取数据的简单程序
 */
public class MultithreadFetcher {

	/** 阻塞队列的最大长度,防止内存溢出。  */
	public static final int MAX_QUEUE_SIZE = 100;
	/** 最大递归深度,防止递归死循环  */
	public static final int RECURSION_LEVEL = Integer.MAX_VALUE;
	/** 致命毒药,优雅关闭后续的工作线程  */
	private static final File DEADLY_POISON = new File("./deadly.tmp");

	/**
	 * 递归遍历文件夹,将遍历的文件放入队列中。
	 * @param folder 目标文件夹
	 * @param fileQueue 文件队列
	 * @param level 递归深度
	 */
	private static void visitFolder(File folder, BlockingQueue<File> fileQueue, int level) throws InterruptedException{
		if(level<=0){//控制递归深度,防止递归死循环。
			return;
		}
		File[] files = folder.listFiles();
		for(File file : files){
			if(file.isDirectory()){
				visitFolder(file,fileQueue,level-1);
			}else if(file.getName().toLowerCase().endsWith(".xml")){
				fileQueue.put(file);
			}else{
				//do nothing ...
			}
		}
	}
	/**
	 * 创建目标文件。通过原文件的名称创建一个新的文件。
	 * @param file 原始文件
	 * @param targetFolder 目标文件夹
	 * @return 新的文件,目标文件
	 */
	private static File createTargetFile(File file, File targetFolder){
		String targetFileName = file.getName();
		return new File(targetFolder,targetFileName);
	}
	/**
	 * 处理文件的操作,可以在这个里面读取文件数据,解析文件,抓取网页,写入备份。
	 * @param file 原始文件,待解析的文件
	 * @param target 目标文件,备份文件
	 */
	private static void travelFile(File file, File target) throws Throwable{
		//详细操作从略
	}

	/** 递归文件夹的线程。不支持多线程并发递归。 */
	static class VisitFolderThread extends Thread{
		private File folder;
		private BlockingQueue<File> fileQueue;
		public VisitFolderThread(File folder, BlockingQueue<File> fileQueue) {
			super("visit-folder-thread");
			this.folder = folder;
			this.fileQueue = fileQueue;
		}
		@Override
		public void run() {
			try {
				visitFolder(folder, fileQueue, RECURSION_LEVEL);
				fileQueue.put(DEADLY_POISON);//放置毒药,优雅关闭
			} catch (InterruptedException e) {
				// 在这里可以做一些异常处理
				e.printStackTrace();
			}
		}
	}

	/** 处理文件的线程,可以多线程并发处理,每个线程处理一个文件  */
	static class TravelFileThread extends Thread{
		private static final AtomicInteger ThreadCount = new AtomicInteger();
		private File targetFolder;
		private BlockingQueue<File> fileQueue;
		public TravelFileThread(File targetFolder, BlockingQueue<File> fileQueue) {
			super("travel-file-thread-"+ThreadCount.incrementAndGet());
			this.targetFolder = targetFolder;
			this.fileQueue = fileQueue;
		}
		@Override
		public void run() {
			File file = null;
			try {
				while((file=fileQueue.take())!=DEADLY_POISON){
					File target = createTargetFile(file, targetFolder);
					try {
						travelFile(file, target);
					} catch (Throwable e) {
						onException(e,file,target);
					}
				}
				fileQueue.put(DEADLY_POISON);//放置毒药,优雅关闭
			} catch (InterruptedException e) {
				// 在这里可以做一些异常处理
				e.printStackTrace();
			}
		}
		/** 在处理文件的过程中,如果抛出异常,则进入下面的处理程序,从略。 */
		private void onException(Throwable e, File file, File target) {
			// 如果travelFile抛出异常,则在此处进行处理。
			e.printStackTrace();
		}
	}

	private BlockingQueue<File> fileQueue = new LinkedBlockingQueue<File>(MAX_QUEUE_SIZE);
	private Thread visitFolderThread;
	private Thread[] travelFileThreads;

	public MultithreadFetcher(File sourceFolder, File targetFolder, int travelThreads) {
		super();
		visitFolderThread = new VisitFolderThread(sourceFolder, fileQueue);
		travelFileThreads = new TravelFileThread[travelThreads];
		for(int i=0;i<travelFileThreads.length;i++){
			travelFileThreads[i] = new TravelFileThread(targetFolder, fileQueue);
		}
	}

	/**
	 * 开始执行
	 */
	public void start(){
		visitFolderThread.start();
		for(int i=0;i<travelFileThreads.length;i++){
			travelFileThreads[i].start();
		}
	}
	/**
	 * 强行终止。请慎用。程序会自动关闭
	 */
	public void terminate(){
		visitFolderThread.interrupt();
		for(int i=0;i<travelFileThreads.length;i++){
			travelFileThreads[i].interrupt();
		}
	}

	/**
	 * 测试用例
	 */
	public static void main(String[] args) {
		final File sourceFolder = new File("");
		final File targetFolder = new File("");
		final int travelThreads = 20;
		MultithreadFetcher fetcher = new MultithreadFetcher(sourceFolder,targetFolder,travelThreads);
		fetcher.start();
	}

}

采取了数据分离的形式,消除了线程间互斥。效率不错。

时间: 2024-11-05 16:30:36

一个简单高效的多线程解决方案的相关文章

[转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程

一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http://blog.csdn.net/locape/article/details/6040383 http://www.cnblogs.com/liuweijian/archive/2009/12/30/1635888.html 一.什么是多线程? 当我自己提出这个问题的时候,我还是很老实的拿着操作系

一个简单的java多线程例子

现在有这样一个任务,有一份手机号列表(20W),有一份话单的列表(10W),要统计哪些手机号没有出现在话单中,哪些手机号在话单中出现了不止一次. 想到的最直接的方式,就是两层循环去遍历,虽然此方法比较笨,但目前还没有想出更好的办法. 一开始使用单线程来处理,代码是随手写的并没有进行重构,只是做一个简单的说明: package tool; import java.util.List; public class SingleThread { public static void main(Strin

JointCode.Shuttle,一个简单高效的跨 AppDomain 通信的服务框架

JointCode.Shuttle 是一个用于 AppDomain 间通信的服务架构. 1. 什么情况下使用 JointCode.Shuttle 在 .net / mono 开发中,一般不太需要使用额外的 AppDomain,但在一些 特定情况下,让代码运行在新的 AppDomain 中也许是一个好的选择. 当代码需要跨越 AppDomain 边界访问另一个 AppDomain 时,便产生了跨 AppDomain 通信的问题,JointCode.Shuttle 正是专为此目的而开发的一个服务框架

一个简单的synchronized多线程问题、梳理与思考

一个程序,多个线程同时操作一个变量,给这个变量+1().功能很简单,可是怎么样去实现呢?这其中涉及到了哪些问题? 最基础想法 见代码: 1 public class Test extends Thread { 2 public static int amount = 0; 3 4 public void run() { 5 amount++: 6 } 7 8 public static void main(String[] args) { 9 int num_thread = 100; 10 f

-一个简单的数据缓存解决方案

最近做了一个聊天室系统,系统有N个聊天室,在并发量很大的情况下,如果用户聊天记录直接写入MySql的话,对数据库的冲击会很大.这里我写了个简单的解决方案:聊天数据先存入redis的队列中,然后用crontab每隔1分钟执行PHP脚本,把数据从redis队列中"批量"移到mysql.当有多条插入语句时,把数据拼装成1条insert语句,减少数据库的操作次数. 1. 配置文件 config.php <?php function dbMessageConfig(){ //用户信息库表的

一个简单的多进程+多线程+协程的例子

因为一个朋友最近想搞接口压力测试,推荐了jmeter,因为jmeter开源,且有命令行启动模式,方便封装.兴起时,自己也简单实现了一下高并发的脚本. 采用的是多进程+多线程+协程.想法是这样的,多进程是为了有效利用多核,理论上最好一个核对应一个进程比较好:那我为什么还要用多线程呢?不怕GIL全局锁吗?这是因为我用了gevent处理,请求采用requests,但requests是阻塞的方法,所以我把requests操作丢到协程做,就没啥问题了.接下来看看脚本,实现了一个2000并发量的脚本(写的比

Golang中使用heap编写一个简单高效的定时器模块

定时器模块在服务端开发中非常重要,一个高性能的定时器模块能够大幅度提升引擎的运行效率.使用Golang和heap实现一个通用的定时器模块,代码来自:https://github.com/xiaonanln/goTimer 也可以查看文档:http://godoc.org/github.com/xiaonanln/goTimer,下面是完整的代码,并进行适当的注释和分析. 从性能测试结果来看,基于heap的定时器模块在效率上并不会比时间轮(TimeWheel)的实现慢多少,但是逻辑上要简单很多.

论PHP模板的简单高效实现

大家都知道PHP是世界上最好的语言,PHP在项目开发中的灵活性是个非常重要的优点,非常适合经常变动的业务逻辑和页面内容,当然都离不开一个好用的模板引擎,市面上最常见的PHP模板引擎是smarty,但是smarty功能十分丰富,有些重量级了.那有没有更好的模板引擎呢? 先来探讨一下模板引擎的几大特点: 书写简单,执行速度,逻辑表达,方便扩展等.从这几方面看最适合的模板引擎就是PHP本身了,所有的要求都能满足要求,不管是smarty还是其他的模板引擎,在执行速度,逻辑表达,方便扩展的方面都不可能超过

MAC COCOA一个简单的多线程程序

功能: 实现多线程:2个线程同时工作,一个用时间计数器,一个用来打印信息 STEP1 XCODE ->New Application ->Cocoa中的Command Line 自动增加: #include <CoreFoundation/CoreFoundation.h> STEP2 // // main.c // test_runloop1 // // Created by DMD on 20/6/14. // Copyright (c) 2014 EDU. All right