做网站无赖客户退款,网站建设的需要是什么,vps建设网站,seo网站结构图本篇文章将对项目搜索引擎#xff08;1#xff09;~#xff08;3#xff09;进行性能优化#xff0c;包括测试#xff0c;优化思路#xff0c;优化前后对比
目录 一#xff1a;文件读取
二#xff1a;实现多线程制作索引
1#xff1a;代码分析
2#xff1a;代码…本篇文章将对项目搜索引擎1~3进行性能优化包括测试优化思路优化前后对比
目录 一文件读取
二实现多线程制作索引
1代码分析
2代码测试
1第一次测试
2第二次测试
3测试总结
三多线程实现代码
1线程池的选用
2索引save执行时机
1问题分析
2解决思路
四线程安全问题
1索引结构中新增文档线程安全分析
2buildForward方法内部代码分析
3builderInverted方法构建倒排索引内部代码分析
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枚举所有文件ArrayListFile 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() {Overridepublic 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方法那里加锁这里加锁的话你并发执行又变成串行了 2buildForward方法内部代码分析 3builderInverted方法构建倒排索引内部代码分析 4加锁对象的创建原理
如果这两个方法的加锁对象为thisindex明显是不很合理的因为正排和倒排是两个不同的对象所以我们可以设置不同的加锁对象这样效率会更好所以这里我们创建了两个不同的加锁对象 //创建两个锁对象private Object locker1 new Object();private Object locker2 new Object();
就好比
5优化结果对比
时间的提升还是非常明显了提升了近1倍的速度 t1是用来衡量解析全部html文件中解析全部Url所耗费的时间
t2则是解析全部content所耗费的时间这里之所以不采用打印的方式是因为打印本身就是一个耗时的操作所以用累加的方式精确到纳秒
6思考是否线程数量越多越好呢
不是的线程数量越多其实彼此间的锁竞争越激烈优化的空间很小了4个线程数量再往上提提升不大了
7守护线程
1现象
我们线程执行完毕了但是进程还没有退出。 2关于守护线程
如果一个线程是守护线程后台线程那么它的运行状态是不会影响进程结束的
相反一个线程是非守护线程它的运行状态是会影响到进程结束的。
通俗一点举例理解 我们用这行代码创建出来的是非守护线程当我们这些线程执行的任务结束后这些线程还是处于整装待发的状态——等你提交任务所以线程的这种状态就会影响到进程的结束 //手动干掉非守护线程executorService.shutdown();
这里我们手动干掉创建出来的这些线程女少~