ThreadPoolExecutor并行编程Demo

本文介绍了一种使用多线程技术加速大规模日志文件处理的方法,并通过具体案例展示了如何利用Java多线程框架提高日志信息抽取效率。

    今天要把线上600M的日志进行信息抽取,提取出被多次编码的中文账号。日志文件有200W行,用单线程的程序跑,花了20分钟。感觉速度不够快,而且CPU的使用率一直是25%, 根本没有有效的利用硬件资源。于是尝试用多线程的程序进行提速,CPU耗到了接近100%, 程序内存耗到了200M,最终花了14分钟把数据跑完,提速了6分钟,感觉还不错。因为对JDK的多线程框架还不熟悉,多线程的代码改写了好几版,中间遇到了各种各样的错误。把最好的一版拿出来,分享下,同时也做个笔记。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.util.HashSet;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test {
	static HashSet<String>	set	= new HashSet<String>();
	static PrintWriter		pw	= null;
	static Pattern			p	= Pattern.compile("\".+dd_sdk_apk[^%]+pin\":\"([^%]*%25.+)\"");

	public static void main(String[] args) {
                int        coreSize   = Runtime.getRuntime().availableProcessors();
		ThreadPoolExecutor es = new ThreadPoolExecutor(coreSize, coreSize * 2, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(100000),
				new ThreadPoolExecutor.CallerRunsPolicy());

		try {
			String log = null;
			pw = new PrintWriter(new File("E:/chinese_user6.txt"));
			InputStreamReader fis = new InputStreamReader(new FileInputStream("E:\\data\\export\\Logs\\im.jd.com\\tracker.log"));
			BufferedReader br = new BufferedReader(fis);

			while ((log = br.readLine()) != null) {
				es.execute(new Task(log));
			}
                        es.shutdown();
			es.awaitTermination(5, TimeUnit.MINUTES);

			fis.close();
			br.close();
			pw.close();
			System.out.println("--------------------------finish!-------------------------------------");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static String decode(String codedName) {
		String lastName = null;
		try {
			for (int idx = 0;; idx++) {
				lastName = codedName;
				codedName = URLDecoder.decode(codedName, "UTF-8");
				if (codedName.equals(lastName)) {
					return codedName;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return codedName;
	}

	private static class Task implements Runnable {
		private String	log	= null;

		public Task(String value) {
			this.log = value;
		}

		@Override
		public void run() {
			try {
				Matcher matcher = p.matcher(log);
				if (matcher.find()) {
					String codedName = matcher.group(1);
					if (codedName.length() < 200) {
						String trueName = decode(codedName);
						if (!set.contains(trueName)) {
							set.add(trueName);
							pw.println(trueName);
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
	}
}

    简单的注释下,LinkedBlockingQueue,是链式阻塞队列。一旦队列装满了,就没法再向里面放任务了。这时候采取ThreadPoolExecutor.CallerRunsPolicy的策略,从字面上理解就是,装不下的任务交由线程的调用者(caller)来运行(run)。这样可以避免装不下的任务被扔掉!

    new LinkedBlockingQueue<Runnable>(100000),这个参数的泛型类型一定要是Runnable,不是你自己实现Runnable的那个类 。

    es.execute(new Task(log));这行代码大家可能认为每读取一行,java进程就会创建一个新的线程。非也!ThreadPoolExecutor是一个线程池,每次扔进去的是一个任务,这个任务是拿线程池里的线程来跑,跑完之后,线程还在那里,任务被扔掉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bruce128

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值