马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本篇文章将对项目搜刮引擎(1)~(3)举行性能优化,包罗测试,优化思绪,优化前后对比
目次
一:文件读取
二:实现多线程制作索引
1:代码分析
2:代码测试
(1)第一次测试
(2)第二次测试
(3)测试总结
三:多线程实当代码
1:线程池的选用
2:索引save实验机会
(1)题目分析
(2)办理思绪
四:线程安全题目
1:索引结构中新增文档线程安全分析
2:buildForward方法内部代码分析
3:builderInverted方法构建倒排索引内部代码分析
4:加锁对象的创建原理
5:优化效果对比
6:思考是否线程数量越多越好呢?
7:保卫线程
(1)征象
(2)关于保卫线程
一:文件读取
在举行文档正文分析的时间我们使用BufferReader来举行文件读取
可以明白为,BufferReader提供了一个缓冲区,每次文档可以加载一部门内容到内存中的缓冲区(默认巨细是8192字节)内里,BufferReader就可以直接从内存中读了,淘汰了硬盘的IO操纵,
- try (BufferedReader bufferedReader = new BufferedReader(new FileReader(f), 1024 * 1024)) {//缓冲区设置为1M,默认的为8192字节太小
- // FileReader fileReader = new FileReader(f);//这里是从硬盘读,我们改成提前读好,之后从内存中读效率会更高
复制代码 我们的HTMl文档比力大,就设置为1M巨细了
二:实现多线程制作索引
1:代码分析
思考:我们的的制作索引方法中核心的三步是,摆列文件,分析文件(包罗分析标题,url,正文),生存文件。
2:代码测试
- public static void main(String[] args) throws IOException, InterruptedException {
- Parser parser = new Parser();
- parser.run();
- // parser.runByThread();//制作索引
- }
复制代码 (1)第一次测试
(2)第二次测试
(3)测试总结
过细的小同伴能发现,同样是run方法单线程制作索引,第一次和第二次测试,时间相差7s悬殊,这里着实跟电脑第一次启动有关,后续我会单独拿出来解说优化
这里我们先看用第二次测试效果:很显着,遍历分析文件淹灭的时间非常大,由于我们要对每一个文件举行:读文件+分词+分析内容。这里我们可以举行优化
三:多线程实当代码
- public void runByThread() throws InterruptedException {
- long beg = System.currentTimeMillis();
- System.out.println("制作索引开始!");
- //1:枚举所有文件
- ArrayList<File> files = new ArrayList<>();
- enumFile(INPUT_PATH, files);
- long endEnumFile = System.currentTimeMillis();
- System.out.println("枚举文件完毕,消耗时间为:" + (endEnumFile - beg) + "ms");
- //2:循环遍历文件,多线程制作索引
- CountDownLatch latch = new CountDownLatch(files.size());//计数锁存器
- ExecutorService executorService = Executors.newFixedThreadPool(4);//线程池
- for (File f : files) {
- executorService.submit(new Runnable() {
- @Override
- public void run() {
- System.out.println("开始解析" + f.getAbsolutePath());
- parseHTML(f);
- latch.countDown();
- }
- }); //解析每一个html文件
- }
- //await方法会阻塞,直到所有选手都调用contDown撞线之后,才能阻塞结束
- latch.await();
- //手动干掉非守护线程
- executorService.shutdown();
- long endFor = System.currentTimeMillis();
- System.out.println("遍历文件完毕!消耗时间为:" + (endFor - endEnumFile) + "ms");
- //3:把在内存中构造好的索引数据结构,保存到指定的文件中
- index.save();
- long end = System.currentTimeMillis();
- System.out.println("多线程下索引制作完毕!消耗总时间为:" + (end - beg) + "ms");
- System.out.println("t1:" + t1 + ", t2:" + t2);
- }
复制代码 1:线程池的选用
不消ThreadPoolExecutor,这内里我们要设置的参数太多啦,包罗核心线程数,最大线程数,存活时间,时间单元,工作任务,线程工厂太多了
这里我们使用Executor中的静态工厂方法newFixedThreadPool,只用传参线程数量参数即可,返回范例为ExecutorService,它是一个接口,继承于Executor接口。
可以通过ExecutorService范例变量的引用来调用线程池的各种方法,比方:任务提交,任务实验,线程池关闭。
2:索引save实验机会
(1)题目分析
这里我们用了4个线程来并发分析我们html文件,那么题目来了,是否会存在submit把文件都提交完毕了,但是线程池还没分析完这些文档,就举行save索引生存方法了呢?显然是有大概的。
那这里我们要确保全部的文档都被分析完了之后,才举行save,雷同活动会跑步比赛,我们要等末了一名选手撞线了才气公布比赛竣事~~
(2)办理思绪
这里我们使用了计数锁存器CountDownLatch,先记载下摆列出来了全部文件个数,每分析完毕一个文件,就countDown一次,只有countDown到0之后才不会举行壅闭,也就是latch.await才会放行!!
四:线程安全题目
三个分析方法不涉及共同对象的修改,因此不存在线程安全题目
1:索引结构中新增文档线程安全分析
不能在addDoc方法那里加锁,这里加锁的话,你并发实验又变成串行了
2:buildForward方法内部代码分析
3:builderInverted方法构建倒排索引内部代码分析
4:加锁对象的创建原理
如果这两个方法的加锁对象为this(index),显着是不很公道的,由于正排和倒排是两个差别的对象,以是我们可以设置差别的加锁对象,如许服从会更好,以是这里我们创建了两个差别的加锁对象
- //创建两个锁对象
- private Object locker1 = new Object();
- private Object locker2 = new Object();
复制代码 就好比
5:优化效果对比
时间的提拔还黑白常显着了,提拔了近1倍的速率
t1是用来权衡分析全部html文件中,分析全部Url所淹灭的时间
t2则是分析全部content所淹灭的时间,这里之以是不采取打印的方式,是由于打印本身就是一个耗时的操纵,以是用累加的方式正确到纳秒
6:思考是否线程数量越多越好呢?
不是的,线程数量越多,着实相互间的锁竞争越猛烈,优化的空间很小了,4个线程数量再往上提提拔不大了
7:保卫线程
(1)征象
我们线程实验完毕了,但是进程还没有退出。
(2)关于保卫线程
如果一个线程是保卫线程(配景线程),那么它的运行状态是不会影响进程竣事的
相反,一个线程黑白保卫线程,它的运行状态是会影响到进程竣事的。
普通一点举例明白:
我们用这行代码创建出来的黑白保卫线程,当我们这些线程实验的任务竣过后,这些线程照旧处于整装待发的状态——等你提交任务,以是线程的这种状态就会影响到进程的竣事!
- //手动干掉非守护线程
- executorService.shutdown();
复制代码 这里我们手动干掉创建出来的这些线程,女少!~
|