【多线程】线程池核心数到底如何配置?

🥰🥰🥰来都来了,不妨点个关注叭!
👉博客主页:欢迎各位大佬!👈

在这里插入图片描述

文章目录

  • 1. 前置回顾
  • 2. 动态线程池
    • 2.1 JMX 的介绍
      • 2.1.1 MBeans 介绍
    • 2.2 使用 JMX + jconsole 实现动态修改线程池
      • 2.2.1 介绍 ManagementFactory
      • 2.2.2 JMX 与 ManagementFactory 的区别与联系

回顾这期内容:【多线程】线程池,介绍了线程池,其中 5.3 介绍如何给线程池设置合适线程数量(这期内容会再回顾一遍),这期内容,具体讨论一下,线程池核心数到底如何配置~

1. 前置回顾

在实际开发中如何给线程池设置合适的线程数量呢?

我们要知道,线程不是越多越好,线程的本质上是要在 CPU 上调度的,一个线程池的线程数量设置为多少合适,这需要结合实际情况实际任务决定,一般分为 CPU 密集型和 IO 密集型,通常是如下配置:

  1. CPU 密集型任务:N+1,主要做一些计算工作,要在 CPU上运行
  2. IO 密集型任务:2N+1,主要是等待 IO 操作,比如等待读写硬盘,读写网卡等,不怎么消耗 CPU 资源
    (其中 N 为 CPU 的核心数量)

Q:为什么要加 1

A:+1 个线程是为了在某个线程因为一些原因,比如缓存未命中、遇到短暂的指令停顿等,暂时阻塞时,能有一个额外的线程可以在 CPU 上运行,从而充分利用 CPU 的空闲时间,避免 CPU 核心出现空闲等待的情况,提高整体的 CPU 利用率,应对一些特殊情况或系统开销等,确保即使在某些线程出现意外阻塞等情况时,系统仍能有额外的线程来维持一定的处理能力,保证系统的稳定性和性能

极端情况下,如果线程全是使用 CPU 运行,线程数就不应该超过 CPU 核心数(逻辑核心,比如一个电脑是6核12线程,即12个逻辑核心,以12为基准),如果线程全是使用的 IO,则线程数可以设置很多,远远超出 CPU 的核心数

在实际开发中,很少有这么极端的情况,就是以实际情况来设定,需要具体通过测试的方式来确定,测试方式的大体思路是,运行程序,通过记录时间戳计算一下执行时间,同时监测资源的使用状态,线程数量取一个执行效率可以并且占用资源也还可以的数量~

这里的两个关键点:

  • 记录时间戳计算执行时间
  • 监测资源的使用状态

计算执行时间的具体操作方法是,可以统计一个完整的请求中,耗费 CPU 计算的过程占用了多少时间,等待的过程,如读取缓存、读取 DB 等占用了多少时间,假设统计结果是 100ms 用来做 CPU 计算,900ms 都是 IO 相关的操作,不占 CPU 时间,那么就可以通过 (100+900) / 100 的计算公式,得出对于单核 CPU,设置线程数为 10 就可以把 CPU 跑满,同理,如果是 6 核 CPU,那么就设置线程数为 60

监测资源的使用状态具体操作方法,在程序执行过程中,持续监测 CPU、内存、线程等资源的使用情况

具体代码如下:

import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ResourceMonitoringExample {// 这里进行模拟任务static class Task implements Runnable {@Overridepublic void run() {try {// 模拟耗时操作Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {int threadCount = 10; // 初始线程数量ExecutorService executor = Executors.newFixedThreadPool(threadCount);// 记录开始时间long startTime = System.currentTimeMillis();// 提交任务for (int i = 0; i < 20; i++) {executor.submit(new Task());}// 关闭线程池executor.shutdown();while (!executor.isTerminated()) {// 监测资源使用状态monitorResources();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}// 记录结束时间long endTime = System.currentTimeMillis();long executionTime = endTime - startTime;System.out.printf("程序执行时间: %d 毫秒%n", executionTime);}// 监测资源使用状态private static void monitorResources() {// 监测 CPU 使用情况OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();if (osBean instanceof com.sun.management.OperatingSystemMXBean) {com.sun.management.OperatingSystemMXBean sunOsBean = (com.sun.management.OperatingSystemMXBean) osBean;double cpuLoad = sunOsBean.getSystemCpuLoad();if (cpuLoad >= 0) {System.out.printf("当前系统 CPU 使用率: %.2f%%%n", cpuLoad * 100);}}// 监测线程数量ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();int threadCount = threadBean.getThreadCount();System.out.printf("当前活动线程数量: %d%n", threadCount);}
}

其中:ManagementFactory 是 Java 中的一个实用工具类,位于 java.lang.management 包下,该类提供了一系列静态方法,用于获取 JVM 的各种管理接口实例,这些管理接口允许你监控和管理 JVM 的运行时状态、系统资源使用情况等(后文会详细介绍)~

总结:

线程池的核心参数并不是这么冰冷冷且固定的数字,还是需要结合实际场景考虑,一般线程池核心数,可以根据实际情况进行计算后配置~

下面介绍动态线程池,我们一起来看看~

2. 动态线程池

线程池线程数的设置是个难题,线程池核心数到底如何配置?最好的办法是能够动态调整线程池线程数,并能够看到调整后的效果,也就是线程利用率,有一个工具能够实现这样的效果 —— 使用 JMX

那么 JMX 是什么呢? 我们先来一起了解一下!

2.1 JMX 的介绍

  1. JMX 是什么:JMX(Java Management Extensions)是 Java 平台的一部分,它提供了一种管理和监控 Java 应用程序的标准方法,即是一个为应用程序、设备、系统等植入管理功能的框架,JMX 允许监控和管理系统资源、应用程序和服务,以及获取关于这些实体的运行时信息,简单来说,就是通过 JMX 可以动态查看对象的运行信息,并且可以动态修改对象属性~
  2. JMX 的架构:如下图
    在这里插入图片描述

分析这张图我们可以发现,JMX 底层是由很多不同的 MBeans 组成的,即 MBeans 是 JMX 的核心

2.1.1 MBeans 介绍

MBeans 是什么:它们是实现了特定接口的 Java 对象,用于表示可以被监控和管理的资源

MBeans 的类型:可以分为四种不同的类型

  • Standard MBeans
  • Dynamic MBeans
  • Open MBeans
  • Model MBeans

MBeans 的作用:这些 MBeans 的作用就是获取对象的信息,或是修改对象信息,都是通过 MBeans 来完成的

MBeans 的用法所有的 MBeans 都需要注册到 MBeanServer 上,然后再通过一些外部工具如 JMX、Web 浏览器等,就可以去获取或者修改 MBeans 的信息了

补充:
Q:那 MBean Server 是什么呢?
A:这里的 MBean Server 是一个代理,它提供一个注册、检索和操作 MBeans 的 API,它是 JMX 架构中的核心组件,负责管理所有 MBeans 的生命周期

接下来,我们一起来看看如何使用 JMX 诗仙女动态修改线程池

2.2 使用 JMX + jconsole 实现动态修改线程池

1)自定义一个动态线程池

首先,我们先来自定义一个动态线程池,如下:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** Created with IntelliJ IDEA.* Description:* User: 26727* Date: 2025-02-05* Time: 18:41*/
public class DynamicThreadPool {private ThreadPoolExecutor threadPoolExecutor;public DynamicThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);}public ThreadPoolExecutor getThreadPoolExecutor() {return threadPoolExecutor;}public void setCorePoolSize(int corePoolSize) {threadPoolExecutor.setCorePoolSize(corePoolSize);}public void setMaximumPoolSize(int maximumPoolSize) {threadPoolExecutor.setMaximumPoolSize(maximumPoolSize);}
}

动态线程池的相关参数如下,动态线程池其实就是传统的线程传统的线程池对象 ThreadPoolExecutor 封装了一下,并且提供了两个方法 setCorePoolSize 和 setMaximumPoolSize,这样通过这两个方法,我们就可以动态设置线程池的线程数了~

在这里插入图片描述

2)自定义一个 MBean 接口

接下来,我们自定义一个 MBean 这个接口中提供四个方法,分别用来获取或者设置线程数的信息

public interface DynamicThreadPoolMXBean {int getCorePoolSize();void setCorePoolSize(int corePoolSize);int getMaximumPoolSize();void setMaximumPoolSize(int maximumPoolSize);
}

3)自定义类实现 DynamicThreadPoolMXBean 接口

接着,我们自定义类实现 DynamicThreadPoolMXBean 接口,并继承 StandardMBean 类,如下:

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import java.lang.management.ManagementFactory;/*** Created with IntelliJ IDEA.* Description:* User: 26727* Date: 2025-02-04* Time: 23:02*/
public class DynamicThreadPoolMBean extends StandardMBean implements DynamicThreadPoolMXBean {private DynamicThreadPool dynamicThreadPool;public DynamicThreadPoolMBean(DynamicThreadPool dynamicThreadPool) throws Exception {super(DynamicThreadPoolMXBean.class);this.dynamicThreadPool = dynamicThreadPool;registerMBean();}private void registerMBean() {try {MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();ObjectName name = new ObjectName("org.javaboy:type=DynamicThreadPool");mbs.registerMBean(this, name);} catch (Exception e) {e.printStackTrace();}}@Overridepublic int getCorePoolSize() {return dynamicThreadPool.getThreadPoolExecutor().getCorePoolSize();}@Overridepublic void setCorePoolSize(int corePoolSize) {dynamicThreadPool.setCorePoolSize(corePoolSize);}@Overridepublic int getMaximumPoolSize() {return dynamicThreadPool.getThreadPoolExecutor().getMaximumPoolSize();}@Overridepublic void setMaximumPoolSize(int maximumPoolSize) {dynamicThreadPool.setMaximumPoolSize(maximumPoolSize);}
}

【解析】

  • 构造函数接受一个 DynamicThreadPool 类型的参数,用于初始化 dynamicThreadPool 的成员变量
    super(DynamicThreadPoolMXBean.class) 调用父类 StandardMBean 的构造函数,传入 DynamicThreadPoolMXBean 类的 Class 对象,用于指定 MBean 的管理接口,调用 registerMBean() 方法将该 MBean 注册到 JMX 平台 MBean 服务器中,即在构造器中,调用了 registerMBean() 方法,这个方法用来将当前对象注册到 MBeanServer 上~

  • 注册 MBean 方法 registerMBean()ManagementFactory.getPlatformMBeanServer() 获取平台 MBean 服务器的实例,ObjectName 用于唯一标识 MBean,这里使用 org.javaboy:type=DynamicThreadPool 作为 MBean 的名称,
    mbs.registerMBean(this, name) 将当前 DynamicThreadPoolMBean 实例注册到 MBean 服务器中

4)执行代码

最后,就可以启动自己的这段代码了~

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;/*** Created with IntelliJ IDEA.* Description:* User: 26727* Date: 2025-02-04* Time: 23:07*/
public class Main {public static void main(String[] args) throws Exception {DynamicThreadPool dynamicThreadPool = new DynamicThreadPool(2, 6, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10));DynamicThreadPoolMBean mBean = new DynamicThreadPoolMBean(dynamicThreadPool);while (true) {System.out.println("CorePoolSize:"+dynamicThreadPool.getThreadPoolExecutor().getCorePoolSize());System.out.println("MaximumPoolSize:"+dynamicThreadPool.getThreadPoolExecutor().getMaximumPoolSize());try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

为了看到线程池的线程数量,这里使用了一个死循环一直打印线程数量信息,这样一会通过 jconsole 修改线程池信息的时候,就能看到修改的效果了~

程序启动之后,使用 jconsole 连接上当前应用程序,如下图:

(jconsole 使用忘记的小伙伴可回顾往期内容:【多线程】如何使用jconsole工具查看Java线程的详细信息?)

点击 Main 进入连接

在这里插入图片描述
点击 MBean 这个选项卡位置,可以看到刚刚配置的 MBean,右侧的值则可以点击直接修改,修改之后,回到应用程序控制台,可以发现线程相关数据已经发生变化了,实现了动态修改的效果~

在这里插入图片描述
可以看到,控制台信息已经发生变化,如下:

在这里插入图片描述
这样就可以动态修改了!

2.2.1 介绍 ManagementFactory

可以看到上面使用了 ManagementFactory 类,下面具体介绍其用法:

1) 获取线程管理接口实例 —— getThreadMXBean()

返回一个 ThreadMXBean 实例,用于监控和管理 Java 虚拟机中的线程,通过该接口,可以获取线程的各种信息,如线程的状态、CPU 时间、阻塞时间等

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;public class ThreadMonitoringExample {public static void main(String[] args) {// 获取 ThreadMXBean 实例ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();// 获取所有线程的 IDlong[] threadIds = threadMXBean.getAllThreadIds();for (long threadId : threadIds) {// 获取线程信息System.out.println("Thread ID: " + threadId + ", Thread Name: " + threadMXBean.getThreadInfo(threadId).getThreadName());}}
}

2)获取内存管理接口实例 —— getMemoryMXBean()

返回一个 MemoryMXBean 实例,用于监控和管理 Java 虚拟机的内存使用情况。通过该接口,可以获取堆内存和非堆内存的使用情况,还可以触发垃圾回收操作~

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;public class MemoryMonitoringExample {public static void main(String[] args) {// 获取 MemoryMXBean 实例MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();// 获取堆内存使用情况MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();System.out.println("Heap Memory Usage: " + heapMemoryUsage);// 获取非堆内存使用情况MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage();System.out.println("Non-Heap Memory Usage: " + nonHeapMemoryUsage);}
}

3)获取运行时管理接口实例 —— getRuntimeMXBean()

返回一个 RuntimeMXBean 实例,用于获取 Java 虚拟机的运行时信息,如 JVM 的启动时间、系统属性、命令行参数等

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;public class RuntimeInfoExample {public static void main(String[] args) {// 获取 RuntimeMXBean 实例RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();// 获取 JVM 启动时间long startTime = runtimeMXBean.getStartTime();System.out.println("JVM Start Time: " + startTime);// 获取系统属性System.out.println("System Properties: " + runtimeMXBean.getSystemProperties());}
}

ManagementFactory 类为 Java 开发者提供了便捷的方式来监控和管理 JVM 的运行时状态,通过获取不同的管理接口实例,可以深入了解 JVM 的内部运行情况,从而进行性能调优、故障排查等相关工作~

2.2.2 JMX 与 ManagementFactory 的区别与联系

区别:

JMX —— Java 扩展管理,是一个为应用程序、设备、系统等植入管理功能的框架

ManagementFactory —— Java 标准库中用于辅助使用 JMX 功能的实用工具

联系:

  • ManagementFactory 是 JMX 功能使用的便捷入口
    JMX 架构复杂:JMX 定义了一套完整的架构,包括 MBean、MBeanServer等核心组件,使用 JMX 进行管理和监控时,需要涉及多个步骤和类的使用,整体较为复杂,而 ManagementFactory 简化操作,ManagementFactory 类提供了一系列静态方法,通过调用这些方法可以方便地获取各种 JMX 管理接口的实例。如,ManagementFactory.getThreadMXBean() 方法返回的 ThreadMXBean 是一个 JMX 的 MXBean,它允许开发者监控和管理 Java 虚拟机中的线程。这些方法隐藏了底层 JMX 架构的复杂性,使得开发者可以更轻松地使用 JMX 功能~
  • ManagementFactory 实例基于 JMX 标准
    ManagementFactory 所返回的各种管理接口实例(如 MemoryMXBean、RuntimeMXBean 等)都是遵循 JMX 规范的 MXBean,这些 MXBean 定义了一组标准的管理操作和属性,可以通过 JMX 代理进行访问和管理,并且可集成到 JMX 系统,通过 ManagementFactory 获取的管理接口实例可以无缝集成到 JMX 系统中。开发者可以将这些 MXBean 注册到 MBeanServer 上,然后使用 JMX 客户端远程或本地监控和管理 Java 应用程序~
  • ManagementFactory 服务于 JMX 监控和管理目的
    支持监控:JMX 的主要目的之一是对 Java 应用程序进行监控和管理,ManagementFactory 所提供的各种管理接口实例可以提供丰富的监控数据,例如,MemoryMXBean 可以提供 Java 虚拟机的内存使用情况,包括堆内存和非堆内存的使用量、峰值等信息;并且ThreadMXBean 可以提供线程的状态、CPU 时间等信息。
    支持管理:除了监控数据,ManagementFactory 所提供的管理接口实例还支持一些管理操作,例如,MemoryMXBean 可以触发垃圾回收操作,ThreadMXBean 可以获取线程的堆栈跟踪信息等,这些操作可以帮助开发者对 Java 应用程序进行动态管理和故障排查~

在这里插入图片描述

💛💛💛本期内容回顾💛💛💛

在这里插入图片描述

✨✨✨本期内容到此结束啦~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode】5. 贪心算法:买卖股票时机

太久没更了&#xff0c;抽空学习下。 看一道简单题。 class Solution:def maxProfit(self, prices: List[int]) -> int:cost -1profit 0for i in prices:if cost -1:cost icontinueprofit_ i - costif profit_ > profit:profit profit_if cost > i:cost iret…

蓝桥杯思维训练营(三)

文章目录 题目详解680.验证回文串 II30.魔塔游戏徒步旅行中的补给问题观光景点组合得分问题 题目详解 680.验证回文串 II 680.验证回文串 II 思路分析&#xff1a;这个题目的关键就是&#xff0c;按照正常来判断对应位置是否相等&#xff0c;如果不相等&#xff0c;那么就判…

DeepSeek大模型介绍、本地化部署与使用!【AI大模型】

一、DeepSeek 是什么&#xff1f; 1.技术定位 专注大模型与AGI研究&#xff0c;开发高性能基座模型&#xff08;如 DeepSeek LLM 系列&#xff09;&#xff0c;支持长文本、多模态、代码生成等复杂任务。 提供开源模型&#xff08;如 DeepSeek-MoE、DeepSeek-V2&#xff09;…

YK人工智能(六)——万字长文学会基于Torch模型网络可视化

1. 可视化网络结构 随着深度神经网络做的的发展&#xff0c;网络的结构越来越复杂&#xff0c;我们也很难确定每一层的输入结构&#xff0c;输出结构以及参数等信息&#xff0c;这样导致我们很难在短时间内完成debug。因此掌握一个可以用来可视化网络结构的工具是十分有必要的…

React+AI 技术栈(2025 版)

文章目录 核心&#xff1a;React TypeScript元框架&#xff1a;Next.js样式设计&#xff1a;Tailwind CSSshadcn/ui客户端状态管理&#xff1a;Zustand服务器状态管理&#xff1a;TanStack Query动画效果&#xff1a;Motion测试工具表格处理&#xff1a;TanStack Table表单处理…

控件【QT】

文章目录 控件QWidgetenabledgeometrysetGeometry qrcwindowOpacityQPixmapfonttoolTipfocusPolicystyleSheetQPushButtonRadio ButtionCheck Box显示类控件QProgressBarcalendarWidget 控件 Qt中已经提供了很多内置的控件了(按钮,文本框,单选按钮,复选按钮&#xff0c;下拉框…

苹果再度砍掉AR眼镜项目?AR真的是伪风口吗?

曾经&#xff0c;AR游戏一度异常火热&#xff0c;宝可梦go让多少人不惜翻墙都要去玩&#xff0c;但是也没过去几年&#xff0c;苹果被曝出再度砍掉了AR眼镜项目&#xff0c;面对着市场的变化&#xff0c;让人不禁想问AR真的是伪风口吗&#xff1f; 一、苹果再度砍掉AR眼镜项目&…

《redis4.0 通信模块源码分析(一)》

【redis导读】redis作为一款高性能的内存数据库&#xff0c;面试服务端开发&#xff0c;redis是绕不开的话题&#xff0c;如果想提升自己的网络编程的水平和技巧&#xff0c;redis这款优秀的开源软件是很值得大家去分析和研究的。 笔者从大学毕业一直有分析redis源码的想法&…

日期选择控件,时间跨度最大一年。

<el-date-picker v-model"times" type"daterange" unlink-panels :picker-options"pickerOptions" :range-separator"$lang(至)":start-placeholder"$lang(开始)" :end-placeholder"$lang(结束)" :default-tim…

JDK9新特性

文章目录 新特性&#xff1a;1.模块化系统使用模块化module-info.java&#xff1a;exports&#xff1a;opens&#xff1a;requires&#xff1a;provides&#xff1a;uses&#xff1a; 2.JShell启动Jshell执行计算定义变量定义方法定义类帮助命令查看定义的变量&#xff1a;/var…

Vue Router 客户端路由解决方案:axios 响应拦截(跳转到登录页面)

文章目录 引言客户端路由 vs. 服务端路由简单的路由案例术语I Vue Router 提供的组件RouterLinkRouterViewII 创建路由器实例调用 createRouter() 函数创建路由选项III 注册路由器插件通过调用 use() 来完成注册路由器插件的职责对于组合式 API,Vue Router 给我们提供了一些组…

在游戏本(6G显存)上本地部署Deepseek,运行一个14B大语言模型,并使用API访问

在游戏本6G显存上本地部署Deepseek&#xff0c;运行一个14B大语言模型&#xff0c;并使用API访问 环境说明环境准备下载lmstudio运行lmstudio 下载模型从huggingface.co下载模型 配置模型加载模型测试模型API启动API服务代码测试 deepseek在大语言模型上的进步确实不错&#xf…

【Uniapp-Vue3】创建DB schema数据表结构

右键uniCloud文件下的database文件&#xff0c;点击“新建DB schema”&#xff0c;选择模板&#xff0c;修改文件名&#xff0c;点击“创建” 创建完成后会出现对应的文件&#xff0c;进入该文件进行配置 对文件中的必填选项&#xff0c;用户权限&#xff0c;字段进行配置 其…

1-ET框架开发环境与demo运行

所需开发环境 安装Unity模块时&#xff0c;记得安装windows Build Support&#xff08;IL2CPP&#xff09;&#xff0c;否则打包会出问题。 安装visual studio&#xff0c;因为需要安装开发组件&#xff0c;需要选择 下载MongoDB7.0.2并安装 确认MongoDB安装成功 查看计算机…

CTP查询资金费率和手续费没响应

CTP的OnRspQryInstrumentOrderCommRate()和OnRspQryInstrumentCommissionRate()和手续费率和手续费有关系&#xff0c;但是今天我通过重写这两个方法&#xff0c;并且调用ReqQryInstrumentCommissionRate()后没响应&#xff0c;查了半天发现&#xff0c;我应该把响应函数实现写…

Python爬虫实战:一键采集电商数据,掌握市场动态!

电商数据分析是个香饽饽&#xff0c;可市面上的数据采集工具要不贵得吓人&#xff0c;要不就是各种广告弹窗。干脆自己动手写个爬虫&#xff0c;想抓啥抓啥&#xff0c;还能学点技术。今天咱聊聊怎么用Python写个简单的电商数据爬虫。 打好基础&#xff1a;搞定请求头 别看爬虫…

Page Assist实现deepseek离线部署的在线搜索功能

前面文章Mac 基于Ollama 本地部署DeepSeek离线模型 实现了deepseek的离线部署&#xff0c;但是部署完成虽然可以进行问答和交互&#xff0c;也有thinking过程&#xff0c;但是没办法像官方一样进行联网搜索。今天我们介绍一款浏览器插件Page Assist来实现联网搜索&#xff0c;完…

Qt跨屏窗口的一个Bug及解决方案

如果我们希望一个窗口覆盖用户的整个桌面&#xff0c;此时就要考虑用户有多个屏幕的场景&#xff08;此窗口要横跨多个屏幕&#xff09;&#xff0c;由于每个屏幕的分辨率和缩放比例可能是不同的&#xff0c;Qt底层在为此窗口设置缩放比例&#xff08;DevicePixelRatio&#xf…

AI绘画:解锁商业设计新宇宙(6/10)

1.AI 绘画&#xff1a;商业领域的潜力新星 近年来&#xff0c;AI 绘画技术以惊人的速度发展&#xff0c;从最初简单的图像生成&#xff0c;逐渐演变为能够创造出高度逼真、富有创意的艺术作品。随着深度学习算法的不断优化&#xff0c;AI 绘画工具如 Midjourney、Stable Diffu…

逻辑回归原理

逻辑回归是一个分类算法&#xff0c;它可以处理二元分类以及多元分类。虽然它名字里面有“回归”两个字&#xff0c;却不是一个回归算法。 逻辑回归尤其是二元逻辑回归是非常常见的模型&#xff0c;训练速度很快&#xff0c;虽然使用起来没有支持向量机&#xff08;SVM&#xf…