【从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() {
                @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();

这里我们手动干掉创建出来的这些线程,女少!~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/972798.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv12推理详解及部署实现

目录 前言一、YOLOv12推理(Python)1. YOLOv12预测2. YOLOv12预处理3. YOLOv12后处理4. YOLOv12推理 二、YOLOv12推理(C)1. ONNX导出2. YOLOv12预处理3. YOLOv12后处理4. YOLOv12推理 三、YOLOv12部署1. 源码下载2. 环境配置2.1 配置CMakeLists.txt2.2 配置Makefile 3. ONNX导出…

在VS-qt的程序中,后期增加PCH预编译功能,提高编译速度

由于前期创建qt程序的时候未勾选pch功能,导致没有启动预编译的功能. 这种情况下需要增加pch功能应该怎么做? 在项目中增加2个文件 stdafx.h和stdafx.cpp文件 stdafx.h增加qt常用头文件 #pragma once //windows #include <windows.h>//qt常用 #include <QObject&g…

校园网架构设计与部署实战

一、学习目标 掌握校园网分层架构设计原则 理解多业务VLAN规划方法 学会部署认证计费系统 实现基础网络安全防护 二、典型校园网场景 需求分析&#xff1a;某中学需建设新型校园网络 覆盖教学楼/宿舍/图书馆三区域 区分教师/学生/访客网络权限 满足2000终端并发接入 …

阐解WiFi信号强度

WiFi信号强度是指无线网络信号的强度&#xff0c;通常以负数dB&#xff08;分贝&#xff09;来表示。信号越强&#xff0c;dB值越接近零。WiFi信号强度直接影响你的网络速度、稳定性和连接的可靠性。简单来说&#xff0c;WiFi信号越强&#xff0c;你的设备与路由器之间的数据传…

【Quest开发】全身跟踪

软件&#xff1a;Unity 2022.3.51f1c1、vscode、Meta XR All in One SDK V72 硬件&#xff1a;Meta Quest3 最终效果&#xff1a;能像meta的操作室沉浸场景一样根据头盔移动来推断用户姿势&#xff0c;实现走路、蹲下、手势匹配等功能 需要借助UnityMovement这个包 GitHub …

用Chrome Recorder轻松完成自动化测试脚本录制

前言 入门自动化测试,录制回放通常是小白测试首先用到的功能。而录制回放工具也一直是各大Web自动化测试必然会着重提供的一块功能。 早期WinRunner、QTP这样的工具,自动化测试可以说是围绕录制回放开展的。近年像Selenium也提供有录制工具 Selenium IDE,Playwright也包含…

延迟任务的11种实现方式(下)!!

接上文&#xff1a; Redisson的RDelayedQueue Redisson他是Redis的儿子&#xff08;Redis son&#xff09;&#xff0c;基于Redis实现了非常多的功能&#xff0c;其中最常使用的就是Redis分布式锁的实现&#xff0c;但是除了实现Redis分布式锁之外&#xff0c;它还实现了延迟…

大型语言模型训练与优化实战指南(2025最新版)

一、大模型训练四部曲 1.1 预训练&#xff1a;构建语言理解的基石 预训练是模型获取通用语言能力的核心阶段&#xff0c;主流方法包括&#xff1a; 自回归生成&#xff08;如GPT系列&#xff09;&#xff1a;预测下一个词&#xff0c;参数规模可达1.8T掩码语言建模&#xff…

【前端】使用WebStorm创建第一个项目

文章目录 前言一、步骤1、启动2、创建项目3、配置Node.js4、运行项目 二、Node.js介绍 前言 根据前面文章中记录的步骤&#xff0c;已经安装好了WebStorm开发软件&#xff0c;接下来我们就用这个IDE开发软件创建第一个项目。 一、步骤 1、启动 启动软件。 2、创建项目 新建…

QML Image 圆角设置

Image 默认是没有圆角的&#xff0c;但是为了让ui看起来美观&#xff0c;有时需要加上圆角&#xff0c;这里分享一种利用遮罩实现的方法。 import QtQuick 2.15 import QtQuick.Controls 2.15 import QtGraphicalEffects 1.15 import Movie 1.0Card {id:rootwidth: 325height:…

计算机网络抄手 运输层

一、运输层协议概述 1. 进程之间的通信 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。当网络边缘部分的两台主机使用网络核心部分的功能进行端到端的通信时&…

Ubuntu22.04 Deepseek-R1本地容器化部署/内网穿透/OPENWEBUI,打造个人AI助手!

1. 前言 本地部署DeepSeek并实现内网穿透&#xff0c;为家庭成员提供强大的AI支持。通过使用Ollama、Docker、OpenWebUI和Nginx&#xff0c;内网穿透&#xff0c;我们可以轻松实现快速响应和实时搜索功能。 2.软硬件环境 系统&#xff1a;ubuntu22.04, cuda12GPU: RTX3080Ti …

Word接入DeepSeek(API的作用)

1.打开”Word”&#xff0c;点击“文件”。 2.点击“选项”。 3.点击“信任中心”——“信任中心设置”。 4. 勾选”启用所有宏“&#xff0c;点击”确定“。 5.点击“自定义功能区”&#xff0c;勾选上“开发工具”&#xff0c;点击“确定”。 6.返回“文件——开发工具“下的…

有向图的强连通分量: Kosaraju算法和Tarjan算法详解

在上一篇文章中, 我们了解了图的最小生成树算法. 本节我们来学习 图的强连通分量(Strongly Connected Component, SCC) 算法. 什么是强连通分量? 在 有向图 中, 若一组节点内的任意两个节点都能通过路径互相到达(例如 A → B A \rightarrow B A→B 且 B → A B \rightarro…

本地部署MindSearch(开源 AI 搜索引擎框架),然后上传到 hugging face的Spaces——L2G6

部署MindSearch到 hugging face Spaces上——L2G6 任务1 在 官方的MindSearch页面 复制Spaces应用到自己的Spaces下&#xff0c;Space 名称中需要包含 MindSearch 关键词&#xff0c;请在必要的步骤以及成功的对话测试结果当中 实现过程如下&#xff1a; 2.1 MindSearch 简…

网络安全中的机器学习

当涉及到网络安全时&#xff0c;技术一直是保护系统免受攻击和数据泄露的关键。在这篇论文中&#xff0c;我将介绍一些当前在网络安全领域使用的关键技术&#xff0c;包括加密&#xff0c;身份验证和防火墙。 首先&#xff0c;加密是网络安全中最常见的技术之一。加密是指使用算…

从猜想终结到算法革新,弹性哈希开启数据存储新篇章

目录 哈希表的前世今生基本原理从传统到现代&#xff1a;哈希表的演变历程 安德鲁 克拉皮文及其团队的创作历程弹性哈希详解基本原理优点技术细节 漏斗哈希解析基本原理优点技术细节 新算法的实际应用案例电子商务推荐系统金融交易监控系统社交媒体内容过滤物联网设备管理 结论…

STM32 外部中断和NVIC嵌套中断向量控制器

目录 背景 外部中断/事件控制器(EXTI) 主要特性 功能说明 外部中断线 嵌套向量中断控制器 特性 ‌中断线&#xff08;Interrupt Line&#xff09; 中断线的定义和作用 STM32中断线的分类和数量 优先级分组 抢占优先级&#xff08;Preemption Priority&#xff09; …

【运维】源码编译安装cmake

背景&#xff1a; 已经在本地源码编译安装gcc/g&#xff0c;现在源码安装cmake 下载源码 下载地址&#xff1a;CMake - Upgrade Your Software Build System 安装步骤&#xff1a; ./bootstrap --prefix/usr/local/cmake make make install 错误处理 1、提示找不到libmpc.…

机器学习实战(8):降维技术——主成分分析(PCA)

第8集&#xff1a;降维技术——主成分分析&#xff08;PCA&#xff09; 在机器学习中&#xff0c;降维&#xff08;Dimensionality Reduction&#xff09; 是一种重要的数据处理技术&#xff0c;用于减少特征维度、去除噪声并提高模型效率。主成分分析&#xff08;Principal C…