当前位置: 首页 > news >正文

Java并发编程——线程池(基础,使用,拒绝策略,命名,提交方式,状态)

我是一个计算机专业研0的学生卡蒙Camel🐫🐫🐫(刚保研)
记录每天学习过程(主要学习Java、python、人工智能),总结知识点(内容来自:自我总结+网上借鉴)
希望大家能一起发现问题和补充,也欢迎讨论👏👏👏

文章目录

    • 线程池🏊
      • 线程池的好处👍
      • 线程池的创建🏗️
      • 线程池(ThreadPoolExecutor)常见参数🔢
      • 处理任务流程🔃
      • 拒绝策略⭐
        • 使用数据库任务表来自定义拒绝策略
      • 线程池中两种提交方式
      • 线程池命名♂️♀️
      • 线程池状态

线程池🏊

线程池是多线程应用中的一种资源管理技术,它旨在减少创建和销毁线程所带来的开销,并且通过复用已存在的线程来执行任务,提高响应速度。线程池提供了一种限制和管理资源(包括执行一个任务的线程)的方法。

线程池的好处👍

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池的创建🏗️

  1. 使用ThreadPoolExecutor构造函数来创建自定义线程池(建议这种方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险)
  2. 通过使用 java.util.concurrent.Executors 工厂类创建

常用的线程池:

Executors 返回线程池对象特点
FixedThreadPool创建一个固定大小的线程池。如果所有线程都处于活动状态,新任务将在队列中等待,直到有线程可用。
CachedThreadPool创建一个可根据需要创建新线程的线程池,但在前一次构造的线程可用时将重用它们。适用于执行大量短期异步任务的应用程序。
SingleThreadExecutor创建一个单线程化的 Executor,它会确保所有任务都在同一个线程中按顺序执行。
ScheduledThreadPool创建一个支持定时及周期性的任务执行的线程池,类似于 Timer
WorkStealingPool创建一个具有多个任务队列的工作窃取线程池,适用于处理大量可并行的任务。

线程池对象的构造方法:

// LinkedBlockingQueue有界队列
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}// LinkedBlockingQueue 无界队列
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}// SynchronousQueue同步队列
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}// DelayedWorkQueue(延迟阻塞队列)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);
}
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}...
}

线程池(ThreadPoolExecutor)常见参数🔢

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;
}

参数解释

参数解释
corePoolSize线程池的核心线程数量:任务队列未达到队列容量时,最大可以同时运行的线程数量。
maximumPoolSize线程池的最大线程数:任务队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
keepAliveTime当线程数大于核心线程数时,多余的空闲线程存活的最长时间
unit时间单位,使用java.util.concurrent.TimeUnit枚举值来指定时间单位
workQueue任务队列,用来储存等待执行任务的队列
threadFactory线程工厂,用来创建线程,一般默认即可
handler拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务(常见的拒绝策略包括抛出异常、丢弃任务、执行者自身运行任务等。)

工作队列(workQueue):用于存放待执行的任务的阻塞队列。可以使用BlockingQueue接口的任何实现,常见的有:

  • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue:一个基于链表结构的有界阻塞队列。如果构造时未指定容量,则默认容量为Integer.MAX_VALUE,即视为无界队列
  • SynchronousQueue:不存储元素的阻塞队列,每个插入操作必须等待另一个线程的对应移除操作。
  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  • **LinkedBlockingDeque:**一个由链表结构组成的双端阻塞队列。

img

处理任务流程🔃

ThreadPoolExecutor中最关键的execute源码:

public void execute(Runnable command) {// 先检查传入的command是否为空,若空则报错if (command == null)throw new NullPointerException();int c = ctl.get(); // 原子操作,返回线程池状态和线程计数的控制字段值// 1. 如果线程数少于corePoolSize,调用addWorker创建核心线程数if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}// 2. 如果线程数大于corePoolSize,将任务添加进workQueue队列if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();// 2.1 如果检查isRunning的状态为false,则remove这个任务,然后执行拒绝策略if (! isRunning(recheck) && remove(command))reject(command);// 2.2 线程池处于running状态,但是没有线程,则创建线程else if (workerCountOf(recheck) == 0)addWorker(null, false);}// 3.如果放入workQueue失败,则创建非核心线程执行任务,如果创建失败(当前线程总数不小于maximumPoolSize),就会拒绝else if (!addWorker(command, false))reject(command);
}

img

拒绝策略⭐

在Java的ThreadPoolExecutor中,当线程池无法处理新提交的任务时(例如,线程池已关闭或者队列已满),可以配置不同的拒绝策略来决定如何处理这些任务。

拒绝策略详细
ThreadPoolExecutor.AbortPolicy**默认的拒绝策略。**抛出 RejectedExecutionException来拒绝新任务的处理。
ThreadPoolExecutor.CallerRunsPolicy如果使用这种策略,那么当任务被拒绝时,该任务将在调用者的线程中执行,即直接在调用execute方法的线程中运行这个任务。这可能会降低任务提交线程的速度,从而减缓任务提交的速度,给线程池一些时间来处理队列中的任务。
ThreadPoolExecutor.DiscardPolicy该策略会悄悄地丢弃无法处理的任务,既不会抛出异常也不会通知调用者。因此,使用此策略时需要注意可能丢失任务的情况。
ThreadPoolExecutor.DiscardOldestPolicy此策略会丢弃队列中最旧的一个未处理任务,并尝试重新提交当前任务。这意味着最老的任务将从队列中移除,然后尝试将当前任务添加到队列中进行处理。
自定义拒绝策略通过实现接口可以自定义任务拒绝策略。

在日常开发中,我们不希望任务被丢弃,我们就会使用CallerRunsPolicy拒绝策略。

// 被拒绝任务的处理程序,直接在execute方法的调用线程中运行被拒绝的任务,除非执行器已关闭,在这种情况下,任务将被丢弃。
public static class CallerRunsPolicy implements RejectedExecutionHandler {/*** Creates a {@code CallerRunsPolicy}.*/public CallerRunsPolicy() { }/***在调用者的线程中执行任务r,除非执行器已关闭,在这种情况下,任务将被丢弃* @param r 请求执行的可运行任务* @param e 试图执行此任务的执行器*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}}
}

又源代码可知:只要当前程序不关闭就会使用执行execute方法的线程执行该任务。

但是会有一定风险

如果走到CallerRunsPolicy的任务是个非常耗时的任务,且处理提交任务的线程是主线程,可能会导致主线程阻塞,影响程序的正常运行。也可能会造成死锁(如果调用者线程正在等待线程池中的某个任务完成,而这个任务又依赖于调用者线程能够继续运行,那么就会形成循环依赖,进而导致死锁。)

解决方案:

  1. 继续采用CallerRunsPolicy拒绝策略

  2. 在内存允许的情况下,我们可以增加阻塞队列BlockingQueue的大小并调整堆内存以容纳更多的任务,确保任务能够被准确执行。

    1. 1为了充分利用 CPU,我们还可以调整线程池的maximumPoolSize (最大线程数)参数,这样可以提高任务处理速度,避免累计在 BlockingQueue的任务过多导致内存用完。
  3. 持久化思路(自定义)

    1. 设计一个数据库表来存储到数据库中

    2. 使用Redis缓存

    3. 提交到消息队列

使用数据库任务表来自定义拒绝策略
  1. 实现RejectedExecutionHandler接口自定义拒绝策略,自定义拒绝策略负责将线程池暂时无法处理(此时阻塞队列已满)的任务入库(保存到 MySQL 中)。注意:线程池暂时无法处理的任务会先被放在阻塞队列中,阻塞队列满了才会触发拒绝策略。
public class DatabaseRejectedExecutionHandler implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// 保存任务到数据库的方法saveTaskToDatabase(r);}private void saveTaskToDatabase(Runnable task) {// 实现保存任务到数据库的逻辑// 可以使用JDBC或者其他ORM框架如MyBatis、Hibernate等}
}
  1. 继承BlockingQueue实现一个混合式阻塞队列,该队列包含 JDK 自带的ArrayBlockingQueue。另外,该混合式阻塞队列需要修改取任务处理的逻辑,也就是重写take()方法,取任务时优先从数据库中读取最早的任务,数据库中无任务时再从 ArrayBlockingQueue中去取任务。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;public class HybridBlockingQueue<E> implements BlockingQueue<E> {private final ArrayBlockingQueue<E> queue;public HybridBlockingQueue(int capacity) {this.queue = new ArrayBlockingQueue<>(capacity);}// 其他BlockingQueue接口方法的实现...@Overridepublic E take() throws InterruptedException {// 尝试从数据库中取任务E taskFromDb = takeFromDatabase();if (taskFromDb != null) {return taskFromDb;} else {// 如果数据库中没有任务,则从queue中取return queue.take();}}private E takeFromDatabase() {// 实现从数据库中取任务的逻辑// 注意这里需要根据你的E类型来确定如何反序列化或转换成任务对象return null; // 返回null表示数据库中没有任务}@Overridepublic boolean offer(E e) {return queue.offer(e);}@Overridepublic int remainingCapacity() {return queue.remainingCapacity();}// ...其他必须实现的方法,例如put(), poll(), etc.
}

线程池中两种提交方式

  1. execute`方法

void execute(Runnable command): Executor接口中的方

  1. submit方法

<T> Future<T> submit(Callable<T> task);

<T> Future<T> submit(Runnable task, T result);

Future<?> submit(Runnable task);

两个方法区别:

  1. 异常处理

  2. 使用execute()时,未捕获异常导致线程终止,线程池创建新线程替代

  3. 使用submit()时,异常被封装在Future中,线程继续复用。(更加灵活的错误处理机制,允许调用者决定如何处理异常)

  4. 接受参数

  5. 返回值

线程池命名♂️♀️

给线程池命名可以帮助你在调试和监控多线程应用程序时更容易识别不同的线程池。默认情况下创建的线程名字类似 pool-1-thread-n 这样的,没有业务含义,不利于我们定位问题。

  1. 使用ThreadFactory来创建一个带有自定义名称的线程池:
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;/*** 线程工厂,它设置线程名称,有利于我们定位问题。*/
public final class NamingThreadFactory implements ThreadFactory {private final AtomicInteger threadNum = new AtomicInteger();private final String name;/*** 创建一个带名字的线程池生产工厂*/public NamingThreadFactory(String name) {this.name = name;}@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r);t.setName(name + " [#" + threadNum.incrementAndGet() + "]");return t;}
}

线程池状态

线程池有5种状态:

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;
线程池状态详细介绍
RUNNING表示线程池处于正常运行状态,可以接受新的任务并处理已提交的任务。
SHUTDOWN表示线程池不再接受新任务,但会继续执行已经提交的任务直到所有任务完成。通过调用 shutdown() 方法进入此状态。
STOP表示线程池不再接受新任务,并尝试停止正在执行的所有任务。通过调用 shutdownNow() 方法进入此状态。
TIDYING表示所有任务都已完成,工作线程数量为零,即将进入 TERMINATED 状态。这是一个短暂的过渡状态,在这个状态下,terminated() 钩子方法会被调用。
TERMINATED表示线程池完全终止,所有的任务都已完成并且所有的工作线程都已被销毁。

shutdownNow为STOP,并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务。它试图终止线程的方法是通过调用 Thread.interrupt() 方法来实现的,但是这种方法的作用有限,如果线程中没有sleep、wait、Condition、定时锁等应用,interrupt()方法是无法中断当前的线程的。所以,shutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。

相关文章:

Java并发编程——线程池(基础,使用,拒绝策略,命名,提交方式,状态)

我是一个计算机专业研0的学生卡蒙Camel&#x1f42b;&#x1f42b;&#x1f42b;&#xff08;刚保研&#xff09; 记录每天学习过程&#xff08;主要学习Java、python、人工智能&#xff09;&#xff0c;总结知识点&#xff08;内容来自&#xff1a;自我总结网上借鉴&#xff0…...

DilateFormer: Multi-Scale Dilated Transformer for Visual Recognition 中的空洞自注意力机制

空洞自注意力机制 文章目录 摘要1. 模型解释1.1. 滑动窗口扩张注意力1.2. 多尺度扩张注意力 2. 代码3. 流程图3.1. MultiDilatelocalAttention3.2. DilateAttention3.3. MLP 摘要 本文针对DilateFormer中的空洞自注意力机制原理和代码进行详细介绍&#xff0c;最后通过流程图梳…...

二十三种设计模式-适配器模式

适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将不兼容的接口转换成客户端期望的接口&#xff0c;从而使原本因接口不匹配而不能一起工作的类可以协同工作。以下是关于适配器模式的详细介绍&#xff1a; 一、定义及作用 定义&am…...

复用类(2):代理、结合使用组合和继承

1 代理 第三种关系称为代理&#xff0c;这是继承与组合之间的中庸之道&#xff0c;因为我们将一个成员对象置于所要构造的类中&#xff08;就像组合&#xff09;&#xff0c;但与此同时我们在新类中暴露了该成员对象的所有方法&#xff08;就像继承&#xff09;。例如&#xff…...

浅谈云计算07 | 云安全机制

云计算安全机制 一、引言二、加密技术&#xff1a;数据的隐形护盾三、散列机制&#xff1a;数据完整性的忠诚卫士四、数字签名&#xff1a;数据来源与真伪的鉴定专家五、公钥基础设施&#xff08;PKI&#xff09;&#xff1a;信任的基石六、身份与访问管理&#xff08;IAM&…...

【机器学习】零售行业的智慧升级:机器学习驱动的精准营销与库存管理

我的个人主页 我的领域&#xff1a;人工智能篇&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;&#x1f44d;点赞 收藏❤ 在当今数字化浪潮汹涌澎湃的时代&#xff0c;零售行业正站在转型升级的十字路口。市场竞争的白热化使得企业必须另辟蹊径&#xff0…...

深入理解 Entity、VO、QO、DTO 的区别及其在 MVC 架构中的应用

文章背景 在现代软件开发中&#xff0c;我们经常会接触到各种数据结构的概念&#xff0c;比如 Entity、VO&#xff08;Value Object&#xff09;、QO&#xff08;Query Object&#xff09;、DTO&#xff08;Data Transfer Object&#xff09;等。这些概念尽管看似相似&#xff…...

vue集成高德地图API实现坐标拾取功能

安装与配置&#xff1a; 组件 | vue-amapDescriptionhttps://elemefe.github.io/vue-amap/#/zh-cn/introduction/install简介 | vuemap/vue-amap简介https://vue-amap.guyixi.cn/zh-cn/introduction/introduction.html ​​​​我的应用 | 高德控制台高德开放平台官网控…...

Spring Boot Actuator 详细介绍

Spring Boot Actuator 详细介绍 1. 简介 Spring Boot Actuator 是 Spring Boot 提供的一个用于监控和管理应用程序的强大功能模块。它可以帮助我们了解应用程序的运行状况、指标收集、环境信息、日志级别管理等。 2. 添加依赖 2.1 在 pom.xml 中添加以下依赖&#xff1a; …...

联通用户管理系统(一)

#联通用户管理系统&#xff08;一&#xff09; 1.新建项目 如果你是windows的话&#xff0c;界面应该是如下的&#xff1a; 2.创建app python manage.py startapp app01一般情况下&#xff1a;我们是在pycharm的终端中运行上述指令&#xff0c;但是pychrm中为我们提供了工具…...

go chan底层分析

go chan底层分析 底层源码hchanmakechan 方法 环形队列阻塞机制向管道写数据流程图源码 从管道读数据流程图源码 关闭通道 底层源码 hchan type hchan struct {qcount uint // 当前队列中剩余元素个数dataqsiz uint // 环形队列长度&#xff0c;即可以…...

idea上git log面板的使用

文章目录 各种颜色含义具体的文件的颜色标签颜色&#x1f3f7;️ 节点和路线 各种颜色含义 具体的文件的颜色 红色&#xff1a;表示还没有 git add 提交到暂存区绿色&#xff1a;表示已经 git add 过&#xff0c;但是从来没有 commit 过蓝色&#xff1a;表示文件有过改动 标…...

WOA-Transformer鲸鱼算法优化编码器时间序列预测(Matlab实现)

WOA-Transformer鲸鱼算法优化编码器时间序列预测&#xff08;Matlab实现&#xff09; 目录 WOA-Transformer鲸鱼算法优化编码器时间序列预测&#xff08;Matlab实现&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现WOA-Transformer鲸鱼算法优化编…...

dock 制作 python环境

报错 :Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers) 解决方法 配置加速地址 vim /etc/docker/daemon.json 添加以下内容 { "registry-mirror…...

2025第3周 | json-server的基本使用

目录 1. json-server是什么&#xff1f;2. json-server怎么用&#xff1f;2.1 安装2.2 创建db.json2.3 启动服务2.4 查看效果 3. 前端进行模拟交互3.1 创建demo.html3.2 创建demo.js 2025&#xff0c;做想做的事&#xff0c;读想读的书&#xff0c;持续学习&#xff0c;自律生活…...

Autodl转发端口,在本地机器上运行Autodl服务器中的ipynb文件

通过 SSH 隧道将远程端口转发到本地机器 输入服务器示例的SSH指令和密码&#xff0c;将远程的6006端口代理到本地 在服务器终端&#xff0c;激活conda虚拟环境 conda activate posecnnexport PYOPENGL_PLATFORMegljupyter notebook --no-browser --port6006 --allow-root从…...

flutter Get GetMiddleware 中间件不起作用问题

当使用 get: ^5.0.0-release-candidate-9.2.1最新版本时&#xff0c;中间件GetMiddleware各种教程都是让我们在redirect中实现&#xff0c;比如&#xff1a; overrideRouteSettings? redirect(String? route) {return RouteSettings(name: "/companyAuthIndexPage"…...

RabbitMQ(三)

RabbitMQ中的各模式及其用法 工作队列模式一、生产者代码1、封装工具类2、编写代码3、发送消息效果 二、消费者代码1、编写代码2、运行效果 发布订阅模式一、生产者代码二、消费者代码1、消费者1号2、消费者2号 三、运行效果四、小结 路由模式一、生产者代码二、消费者代码1、消…...

【Python】Python之locust压测教程+从0到1demo:基础轻量级压测实战(1)

文章目录 一、什么是Locust二、Locust 架构组成三、实战 Demo准备一个可调用的接口编写一个接口测试用例编写一个性能测试用例执行性能测试用例代码1、通过 Web UI 执行&#xff08;GUI模式&#xff09;2、通过命令行执行&#xff08;非GUI模式&#xff09; 小知识&#xff1a;…...

【JavaScript】基础内容,HTML如何引用JavaScript, JS 常用的数据类型

HTML 嵌入 Javascript 的方式 引入外部 js 文件 <head> <script Language "javaScript" src"index.js"/> </head>内部声明 <head> <script language"javascript">function hello(){alert("hello word&qu…...

vue使用自动化导入api插件unplugin-auto-import,避免频繁手动导入

‌unplugin-auto-import‌是一个现代的自动导入插件&#xff0c;旨在简化前端开发中的导入过程&#xff0c;减少手动导入的繁琐工作&#xff0c;提升开发效率。它支持多种构建工具&#xff0c;包括Vite、Webpack、Rollup和esbuild&#xff0c;并且可以与TypeScript配合使用&…...

在 C# 中的Lambda 表达式

在 C# 中&#xff0c;Lambda 表达式是用来定义匿名函数的一种简洁方式&#xff0c;通常用于简化代码&#xff0c;尤其是在 LINQ 查询、事件处理或方法作为参数的场景中。Lambda 表达式的语法如下&#xff1a; 基本语法 (parameters) > expression_or_statement_blockparam…...

奉加微PHY6230兼容性:部分手机不兼容

从事嵌入式单片机的工作算是符合我个人兴趣爱好的,当面对一个新的芯片我即想把芯片尽快搞懂完成项目赚钱,也想着能够把自己遇到的坑和注意事项记录下来,即方便自己后面查阅也可以分享给大家,这是一种冲动,但是这个或许并不是原厂希望的,尽管这样有可能会牺牲一些时间也有哪天原…...

32单片机综合应用案例——基于GPS的车辆追踪器(三)(内附详细代码讲解!!!)

困难不会永远存在&#xff0c;只要你勇于面对&#xff0c;坚持努力&#xff0c;就一定能够战胜一切困难。每一次挑战都是一次成长的机会&#xff0c;不要害怕失败&#xff0c;失败是成功之母。只有经历过失败&#xff0c;你才能更加明白自己的不足&#xff0c;并不断改进自己&a…...

45_Lua模块与包

Lua中的模块系统是该语言的一个重要特性,它允许开发者将代码分割成更小、更易于管理的部分。通过使用模块,你可以创建可重用的代码片段,并且可以降低代码间的耦合度。下面我将详细介绍Lua模块的基本概念、语法以及一些实际案例。 1.Lua模块 1.1 模块的基本概念 从Lua 5.1…...

深度学习电影推荐-CNN算法

文章目录 前言视频演示效果1.数据集环境配置安装教程与资源说明1.1 ML-1M 数据集概述1.1.1数据集内容1.1.2. 数据集规模1.1.3. 数据特点1.1.4. 文件格式1.1.5. 应用场景 2.模型架构3.推荐实现3.1 用户数据3.2 电影数据3.3 评分数据3.4 数据预处理3.5实现数据预处理3.6 加载数据…...

【Git 】探索 Git 的魔法——git am 与补丁文件的故事

在日常的开发协作中&#xff0c;你可能会遇到这样的场景&#xff1a;某位热心的小伙伴发来一份 .patch 文件&#xff0c;让你把某个问题修复合并到项目中。如果你不知道如何优雅地接收并应用这份补丁&#xff0c;那么这篇文章就是为你准备的&#xff01;让我们一起揭开 Git 的“…...

G1原理—5.G1垃圾回收过程之Mixed GC

大纲 1.Mixed GC混合回收是什么 2.YGC可作为Mixed GC的初始标记阶段 3.Mixed GC并发标记算法详解(一) 4.Mixed GC并发标记算法详解(二) 5.Mixed GC并发标记算法详解(三) 6.并发标记的三色标记法 7.三色标记法如何解决错标漏标问题 8.SATB如何解决错标漏标问题 9.重新梳…...

机器人传动力系统介绍

电驱动系统 无框力矩电机减速器&#xff1a;优点是功率密度高&#xff0c;可在有限空间产生大扭矩&#xff0c;使机器人关节运动有力灵活&#xff0c;如人形机器人四肢运动。缺点是系统复杂&#xff0c;成本高&#xff0c;减速器会降低传动效率.空心杯电机行星滚柱丝杆&#x…...

1161 Merging Linked Lists (25)

Given two singly linked lists L1​a1​→a2​→⋯→an−1​→an​ and L2​b1​→b2​→⋯→bm−1​→bm​. If n≥2m, you are supposed to reverse and merge the shorter one into the longer one to obtain a list like a1​→a2​→bm​→a3​→a4​→bm−1​⋯. For ex…...