【从0做项目】Java搜索引擎(4)——性能优化~烧脑~~~
本篇文章将对项目搜索引擎(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() {@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方法那里加锁,这里加锁的话,你并发执行又变成串行了


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();
这里我们手动干掉创建出来的这些线程,女少!~
相关文章:
【从0做项目】Java搜索引擎(4)——性能优化~烧脑~~~
本篇文章将对项目搜索引擎(1)~(3)进行性能优化,包括测试,优化思路,优化前后对比 目录 一:文件读取 二:实现多线程制作索引 1:代码分析 2:代码…...
【HarmonyOS Next】鸿蒙应用进程和线程详解
【HarmonyOS Next】鸿蒙应用进程和线程详解 一、前言 进程的定义: 进程是系统进行资源分配的基本单位,是操作系统结构的基础。 在鸿蒙系统中,一个应用下会有三类进程: (1) 主进程, (2) ExtensionAbility进程ÿ…...
【前端ES】ECMAScript 2023 (ES14) 引入了多个新特性,简单介绍几个不为人知但却好用的方法
Array.prototype.toSorted() 返回一个新的已排序数组副本,不改变原数组。 let arr [5, 4, 2, 3, 1]; console.log(arr.toSorted()); // [1, 2, 3, 4, 5]Array.prototype.with() 允许根据索引修改数组中的单个元素,并返回新数组。 const arr ["…...
【EndNote】WPS 导入EndNote 21
写在前面:有没有人有激活码,跪求! EndNote,在文献管理和文献引用方面很好用。写文章的时候,使用EndNote引入需要的文献会很方便。我目前用的WPS,想把EndNote的CWYW(Cite While You Write&#…...
网上购物|基于SprinBoot+vue的网上购物系统(源码+数据库+文档)
网上购物系统目录 基于SprinBootvue的网上购物 一、前言 二、系统设计 三、系统功能设计 5.1 管理员功能实现 5.1.1 论坛管理 5.1.2 商品管理 5.1.3 商品评价管理 5.1.4 商品订单管理 5.2 用户功能实现 5.2.1 商品信息 5.2.2 确认下单 5.2.3 商品订单 5.2.4 购物…...
AI 语言模型发展史:统计方法、RNN 与 Transformer 的技术演进
引言 自然语言处理(NLP)是 AI 领域的重要分支,而语言模型(Language Model, LM)是 NLP 的核心技术。语言模型经历了从 统计方法 到 RNN(循环神经网络),再到 Transformer 的演进&…...
Pycharm中查找与替换
1、Edit -> Find -> Find 在当前文件中查找 2、Edit -> Find -> Find in Files 在所有文件中查找 3、Edit -> Find -> Replace 在当前文件中执行替换 4、Edit -> Find -> Replace in Files 在所有文件中执行替换...
有向图的强连通分量: Kosaraju算法和Tarjan算法详解
在上一篇文章中, 我们了解了图的最小生成树算法. 本节我们来学习 图的强连通分量(Strongly Connected Component, SCC) 算法. 什么是强连通分量? 在 有向图 中, 若一组节点内的任意两个节点都能通过路径互相到达(例如 A → B A \rightarrow B A→B 且 B → A B \rightarro…...
mac相关命令
显示和隐藏usr等隐藏文件文件 terminal输入: defaults write com.apple.Finder AppleShowAllFiles YESdefaults write com.apple.Finder AppleShowAllFiles NO让.bashrc每次启动shell自动生效 编辑vim ~/.bash_profile 文件, 加上 if [ -f ~/.bashrc ]; then. ~/.bashrc fi注…...
代码随想录算法训练营第六天| 242.有效的字母异位词 、349. 两个数组的交集、202. 快乐数 、1. 两数之和
242.有效的字母异位词 题目链接:242.有效的字母异位词 文档讲解:代码随想录有效的字母异位词 视频讲解:LeetCode:有效的字母异位词 状态:学会了 思路: 数组其实是简单哈希表。 哈希表用来快速判断元素是否在…...
dify实现分析-rag-关键词索引的实现
概述 在dify中有两种构建索引的方式,一种是经济型,另一种是高质量索引(通过向量数据库来实现)。其中经济型就是关键词索引,通过构建关键词索引来定位查询的文本块,而关键词索引的构建是通过Jieba这个库来完…...
【小白学HTML5】一文讲清常用单位(px、em、rem、%、vw、vh)
html5中,常用的单位有px、em、rem、%、vw、vh(不常用)、cm、m等,这里主要讲解px、em、rem、%、vw。 学习了解:主流浏览器默认的字号:font-size:16px,无论用什么单位,浏览器最终计算…...
Fastgpt学习(5)- FastGPT 私有化部署问题解决
1.☺ 问题描述: Windows系统,本地私有化部署,postgresql数据库镜像日志持续报错" data directory “/var/lib/postgresql/data” has invalid permissions ",“ DETAIL: Permissions should be urwx (0700) or urwx,gr…...
ubuntu下安装TFTP服务器
在 Ubuntu 系统下安装和配置 TFTP(Trivial File Transfer Protocol)服务器可以按照以下步骤进行: 1. 安装 TFTP 服务器软件包 TFTP 服务器通常使用 tftpd-hpa 软件包,你可以使用以下命令进行安装: sudo apt update …...
深入解析 iText 7:从 PDF 文档中提取文本和图像
在现代开发中,PDF 文件的操作是不可避免的一部分。无论是生成报告、解析文档,还是从文件中提取信息,我们常常需要处理 PDF 文件。iText 是一个非常强大的库,广泛应用于 PDF 文件的创建、修改和解析。自 iText 7 发布以来ÿ…...
Rust编程语言入门教程 (六)变量与可变性
Rust 系列 🎀Rust编程语言入门教程(一)安装Rust🚪 🎀Rust编程语言入门教程(二)hello_world🚪 🎀Rust编程语言入门教程(三) Hello Cargo…...
事务--实操演示
目录 一、准备工作 二、在MySQL中操作事务(重点) 第一种方式:使用命令的方式 第二种方式:设置MySQL事务不默认提交的方式 结 三、在JDBC中操作事务(掌握) 第一种方式:使用命令的方式 第…...
PHP是如何并行异步处理HTTP请求的?
文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons:JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram,自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 ? 5 IDEA必装的插件&…...
【Spring详解一】Spring整体架构和环境搭建
一、Spring整体架构和环境搭建 1.1 Spring的整体架构 Spring框架是一个分层架构,包含一系列功能要素,被分为大约20个模块 Spring核心容器:包含Core、Bean、Context、Expression Language模块 Core :其他组件的基本核心ÿ…...
在 Vue 3 中使用 Lottie 动画:实现一个加载动画
在现代前端开发中,动画是提升用户体验的重要元素之一。Lottie 是一个流行的动画库,它允许我们使用 JSON 文件来渲染高质量的动画。本文将介绍如何在 Vue 3 项目中集成 Lottie 动画,并实现一个加载动画效果。 如果对你有帮助请帮忙点个&#x…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...
Xcode 16 集成 cocoapods 报错
基于 Xcode 16 新建工程项目,集成 cocoapods 执行 pod init 报错 ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchro…...
组合模式:构建树形结构的艺术
引言:处理复杂对象结构的挑战 在软件开发中,我们常遇到需要处理部分-整体层次结构的场景: 文件系统中的文件与文件夹GUI中的容器与组件组织结构中的部门与员工菜单系统中的子菜单与菜单项组合模式正是为解决这类问题而生的设计模式。它允许我们将对象组合成树形结构来表示&…...
