Quartz任务调度框架实现任务动态执行

news2025/1/1 22:00:47

说明:之前使用Quartz,都是写好Job,指定一个时间点,到点执行。最近有个需求,需要根据前端用户设置的时间点去执行,也就是说任务执行的时间点是动态变化的。本文介绍如何用Quartz任务调度框架实现任务动态执行。

基本实现

(0)思路

基本思路是,根据用户传入的时间点,转为cron表达式,定时去刷新容器内的JOB,更新任务的cron表达式。

(1)搭建项目

创建一个项目,pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/>
    </parent>

    <groupId>com.hezy</groupId>
    <artifactId>auto_quartz</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.3</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.6</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

我项目的是postgresql,application.yml如下

server:
  port: 8988

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/demo
    username: postgres
    password: 123456
    driver-class-name: org.postgresql.Driver

创建一张任务调度表,包含了任务名(对应Java类中JOB的Bean名称),任务的cron表达式;

-- 创建任务调度表
create table public.t_task
(
    id         varchar(32)       not null
        constraint pc_task_id
            primary key,
    task_name  varchar(50)       not null,
    cron       varchar(50)       not null,
    is_deleted integer default 0 not null
);

comment on table public.t_task is '任务调度表';
comment on column public.t_task.task_name is '任务名';
comment on column public.t_task.cron is 'cron表达式';
comment on column public.t_task.is_deleted is '是否删除,1是,0否,默认0';

(2)引入Quartz

创建两个关于Quartz的配置类

(MyJobFactory)

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;

/**
 * 任务工厂
 */
@Component
public class MyJobFactory extends AdaptableJobFactory {
 
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    /**
     * 创建JOB实例
     */
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object jobInstance = super.createJobInstance(bundle);
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

(TaskConfig)

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * 任务配置类
 */
@Configuration
@DependsOn("myJobFactory")
public class TaskConfig {
 
    @Autowired
    private MyJobFactory myJobFactory;

    /**
     * 加载任务工厂
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(myJobFactory);
        return schedulerFactoryBean;
    }
 
    @Bean
    public Scheduler scheduler() {
        return schedulerFactoryBean().getScheduler();
    }
}

(3) 数据库操作实现

创建一个TaskDTO

import lombok.Data;

@Data
public class TaskDTO {
 
    private String id;

    /**
     * 任务名
     */
    private String taskName;

    /**
     * CRON表达式
     */
    private String cron;
}

写一个对应的Mapper,里面创建一个查询方法

import com.hezy.pojo.TaskDTO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface TaskMapper {
 
    @Select("select id, task_name taskName, cron from t_task where is_deleted = 0")
    List<TaskDTO> getAllTask();
}

插入两条数据,如下:

insert into public.t_task (id, task_name, cron, is_deleted)
values ('1', 'SimpleTask1', '0/5 * * * * ?', 0);

insert into public.t_task (id, task_name, cron, is_deleted)
values ('2', 'SimpleTask2', '0/30 * * * * ?', 0);

测试,没得问题

import com.hezy.mapper.TaskMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class TaskMapperTest {

    @Autowired
    private TaskMapper taskMapper;

    @Test
    public void testInsert() {
        System.out.println(taskMapper.getAllTask());
    }
}

在这里插入图片描述

(4)创建任务容器

创建一个任务容器,每10秒去刷新容器内的所有Job的cron表达式

import com.hezy.constant.TaskConst;
import com.hezy.mapper.TaskMapper;
import com.hezy.pojo.TaskDTO;
import lombok.extern.log4j.Log4j2;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
import java.util.List;

/**
 * 任务容器
 */
@Component
@DependsOn(value = {"quartzManager"})
@Log4j2
public class TaskContext {
 
    @Autowired
    private QuartzManager quartzManager;
 
    @Autowired
    private TaskMapper taskMapper;

    /**
     * 更新任务
     * 刷新频率:10秒
     */
    @Scheduled(fixedRate = 10000)
    public void update() {
        try {
            // 查出所有任务
            List<TaskDTO> taskDTOS = taskMapper.getAllTask();
            // 遍历操作
            for (TaskDTO taskDto : taskDTOS) {
                this.quartzManager.startJobTask(taskDto.getTaskName(), TaskConst.GroupEnum.SYSTEM.name(), taskDto.getCron());
            }
        } catch (SchedulerException e) {
            log.error("初始化定时任务异常", e);
        }
    }
}

需要注意,@Scheduled注解,需要在启动类上加@EnableScheduling注解才能生效

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Start {
    public static void main(String[] args) {
        SpringApplication.run(Start.class, args);
    }
}

创建一个常量,表示任务的组名

(TaskConst)

/**
 * 任务常量
 */
public class TaskConst {

    /**
     * 任务分组
     */
    public enum GroupEnum {
        SYSTEM;
    }
}

任务管理器,对比传入的任务名、任务cron,更新或创建对应的任务;

import lombok.extern.log4j.Log4j2;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 定时任务管理器
 * @author hezhongying
 * @create 2024/12/11
 */
@Configuration
@Log4j2
public class QuartzManager {

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private List<Job> taskList;

    private Map<String, Job> taskMap;

    @PostConstruct
    public void init() {
        this.taskMap = taskList.stream().collect(Collectors.toMap(t -> t.getClass().getSimpleName(), t -> t));
    }

    /**
     * 启动任务
     * @param jobName 任务名
     * @param group 组名
     * @param cron cron表达式
     * @throws SchedulerException
     */
    public void startJobTask(String jobName, String group, String cron) throws SchedulerException {
        // 创建Job
        JobKey jobKey = new JobKey(jobName, group);

        // 判断任务是否已存在,不存在新增,存在更新
        if (scheduler.checkExists(jobKey)) {
            if (modifyJob(jobName, group, cron)) {
                log.info("任务:{} 修改成功", jobName);
            }
        } else {
            if (createJob(jobName, group, cron)) {
                log.info("任务:{} 新增成功", jobName);
            }
        }
    }

    /**
     * 新增任务
     * @param jobName 任务名称
     * @param group 分组
     * @param cron CRON表达式
     * @throws SchedulerException
     */
    private boolean createJob(String jobName, String group, String cron) throws SchedulerException {
        // 任务新增
        if (taskMap.containsKey(jobName)) {
            // 执行任务
            Class<? extends Job> taskClazz = taskMap.get(jobName).getClass();
            JobDetail jobDetail = JobBuilder.newJob(taskClazz).withIdentity(jobName, group).build();

            // 执行时间正则
            CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cron);
            CronTrigger cronTrigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(jobName, group)
                    .withSchedule(cronBuilder)
                    .build();

            scheduler.scheduleJob(jobDetail, cronTrigger);
            return true;
        } else {
            log.debug("任务没有配置执行配置{}", jobName);
        }
        return false;
    }

    /**
     * 修改
     *
     * @param jobName 任务名
     * @param group 组名
     * @param cron cron表达式
     * @return true,修改成功;false,修改失败
     * @throws SchedulerException
     */
    public boolean modifyJob(String jobName, String group, String cron) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(jobName, group);

        Trigger trigger = scheduler.getTrigger(triggerKey);
        if (trigger == null) {
            log.info("未存在的触发器[{}-{}]", jobName, group);
            return false;
        }

        String oldCron = ((CronTrigger) trigger).getCronExpression();
        // 判断cron是否相等,相等就不用改
        if (!cron.equals(oldCron)) {
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            CronTrigger newTrigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(jobName, group)
                    .withSchedule(cronScheduleBuilder)
                    .build();

            Date date = scheduler.rescheduleJob(triggerKey, newTrigger);
            return date != null;
        } else {
            log.info("任务:{} CRON表达式没有变化", jobName);
        }

        return false;
    }

    /**
     * 暂停所有任务
     *
     * @throws SchedulerException
     */
    public void pauseAll() throws SchedulerException {
        this.scheduler.pauseAll();
    }

    /**
     * 指定任务暂停
     *
     * @param jobName 任务名
     * @param group 组名
     * @throws SchedulerException
     */
    public void pause(String jobName, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(jobName, group);
        JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            return;
        }

        this.scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复所有任务
     *
     * @throws SchedulerException
     */
    public void resumeAllJob() throws SchedulerException {
        this.scheduler.resumeAll();
    }

    /**
     * 删除指定任务
     *
     * @param jobName 任务名
     * @param group 组名
     * @throws SchedulerException
     */
    public void delete(String jobName, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(jobName, group);
        JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            return;
        }
        this.scheduler.deleteJob(jobKey);
    }
}

注意这行代码,可自动注入容器内所有的Job,也就是所有实现了Quartz框架Job接口的类;

    @Autowired
    private List<Job> taskList;

(5)创建两个Job

创建两个简单的Job

(SimpleTask1)

import lombok.extern.log4j.Log4j2;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 简单任务1
 */
@Component
@Log4j2
public class SimpleTask1 implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) {
        log.info("{} 定时任务执行时间:{}", "SimpleTask1", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}

(SimpleTask2)

import lombok.extern.log4j.Log4j2;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 简单任务2
 */
@Component
@Log4j2
public class SimpleTask2 implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) {
        log.info("{} 定时任务执行时间:{}", "SimpleTask2", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}

(6)启动测试

启动项目,查看控制台

在这里插入图片描述

此时,项目不停,修改数据对应任务的cron表达式,

在这里插入图片描述

查看控制台

在这里插入图片描述

到这,动态调度任务就实现了。

更进一步

回到我最开始提到的需求,再来考虑一些细节。

  • 前端传递的时间如何转为cron表达式;

  • 如何实现不同任务对同一个Job的动态执行;


第一个问题,前端传递的任务数据,可能是这样的,任务“每天、每周、每月”,然后设置一个时间点,要求任务按照这个周期来执行,如果将这些信息转为对应的cron表达式,需要有一段代码来实现这些映射关系。

如下:

    /**
     * 生成CRON表达式
     * @param type 类型:每天、每周、每月
     * @param hour 时
     * @param minute 分
     * @param dayOfWeek 星期
     * @param dayOfMonth 日期
     * @return CRON表达式或null
     */
    public static String generateCronExpression(int type, int hour, int minute, int dayOfWeek, int dayOfMonth) {
        switch (type) {
            case 1:
                return String.format("0 %d %d * * ?", minute, hour);
            case 2:
                return String.format("0 %d %d ? * %s", minute, hour, TaskConst.DayOfWeekEnum.getByCode(dayOfWeek).getName());
            case 3:
                return String.format("0 %d %d %d * ?", minute, hour, dayOfMonth);
            default:
                return null;
        }
    }

(星期映射)

cron表达式末尾不能填对应星期的数字,而需要填星期几的英文简称

    /**
     * 星期枚举
     */
    public enum DayOfWeekEnum {

        SUNDAY(1, "SUN"),

        MONDAY(2, "MON"),

        TUESDAY(3, "TUE"),

        WEDNESDAY(4, "WED"),

        THURSDAY(5, "THU"),

        FRIDAY(6, "FRI"),

        SATURDAY(7, "SAT");

        private final Integer code;

        private final String name;

        DayOfWeekEnum(Integer code, String name) {
            this.code = code;
            this.name = name;
        }

        public Integer getCode() {
            return code;
        }

        public String getName() {
            return name;
        }

        /**
         * 根据code查询枚举
         */
        public static DayOfWeekEnum getByCode(Integer code) {
            for (DayOfWeekEnum value : DayOfWeekEnum.values()) {
                if (value.getCode().equals(code)) {
                    return value;
                }
            }
            return null;
        }

        /**
         * 根据name查询枚举
         */
        public static DayOfWeekEnum getByName(String name) {
            for (DayOfWeekEnum value : DayOfWeekEnum.values()) {
                if (value.getName().equals(name)) {
                    return value;
                }
            }
            return null;
        }
    }

但这种方式局限于我上面说的,只能“每天、每周、每月”的某时、某分执行,而不能更精准到秒、或者每月的最后一周,这样更灵活的设置。(思考)或许可以考虑直接将cron表达式的设置,开放给前端,由用户直接来设置,后端只需校验cron的合法性即可。


第二个问题,有这样的情况:我的任务调度表中的多条记录,执行的是同一个Job,我的Job中会根据记录的唯一ID去查找这个记录对应的数据来处理。这种情况要怎样,还能实现动态调度。

如下,两条记录,对应同一个Job,但是cron不同,执行周期不同;

在这里插入图片描述

首先,在定时刷新这里,就不能只传入taskName,再传入一个唯一标识,我这里就传入ID;

    /**
     * 更新任务
     * 刷新频率:10秒
     */
    @Scheduled(fixedRate = 10000)
    public void update() {
        try {
            // 查出所有任务
            List<TaskDTO> taskDTOS = taskMapper.taskInfo();
            // 遍历操作
            for (TaskDTO taskDto : taskDTOS) {
                // 带上唯一标识
                this.quartzManager.startJobTask(taskDto.getTaskName(), taskDto.getId(), TaskConst.GroupEnum.SYSTEM.name(), taskDto.getCron());
            }
        } catch (SchedulerException e) {
            log.error("初始化定时任务异常", e);
        }
    }

任务管理器这里,判断是否有当前任务,就用任务名_唯一标识判断,绑定Job的时候,就用Job名,即数据库记录的taskName,如下:

    /**
     * 启动任务
     * @param name Job名,即Job类名
     * @param id 唯一标识
     * @param group 组名
     * @param cron cron表达式
     * @throws SchedulerException
     */
    public void startJobTask(String name, String id, String group, String cron) throws SchedulerException {
        // 任务名,用Job名和唯一标识拼接
        String jobName = String.format("%s_%s", name, id);

        // 创建Job
        JobKey jobKey = new JobKey(jobName, group);

        // 判断任务是否已存在,不存在新增,存在更新
        if (scheduler.checkExists(jobKey)) {
            if (modifyJob(jobName, id, group, cron)) {
                log.info("任务:{} 修改成功", jobName);
            }
        } else {
            if (createJob(name, jobName, id, group, cron)) {
                log.info("任务:{} 新增成功", jobName);
            }
        }
    }

新增Job

    /**
     * 新增任务
     * @param name JOB名
     * @param jobName 任务名称
     * @param id 唯一标识
     * @param group 分组
     * @param cron CRON表达式
     * @throws SchedulerException
     */
    private boolean createJob(String name, String jobName, String id, String group, String cron) throws SchedulerException {
        // 任务新增,用Job名判断当前容器中是否存在这个Job
        if (taskMap.containsKey(name)) {
            // 执行任务:获取Job类时,也用Job名
            Class<? extends Job> taskClazz = taskMap.get(name).getClass();
            // 创建任务式,用Job和唯一标识拼接作为任务名
            JobDetail jobDetail = JobBuilder.newJob(taskClazz).withIdentity(jobName, group).build();

            // 创建 JobDataMap 并放入唯一标识 id
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("id", id);

            // 执行时间正则
            CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cron);
            CronTrigger cronTrigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(jobName, group)
                    .usingJobData(jobDataMap)
                    .withSchedule(cronBuilder)
                    .build();

            scheduler.scheduleJob(jobDetail, cronTrigger);
            return true;
        } else {
            log.debug("任务没有配置执行配置{}", jobName);
        }
        return false;
    }

修改Job

    /**
     * 修改
     *
     * @param jobName 任务名
     * @param group 组名
     * @param cron cron表达式
     * @return true,修改成功;false,修改失败
     * @throws SchedulerException
     */
    public boolean modifyJob(String jobName, String id, String group, String cron) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(jobName, group);

        Trigger trigger = scheduler.getTrigger(triggerKey);
        if (trigger == null) {
            log.info("未存在的触发器[{}-{}]", jobName, group);
            return false;
        }

        // 创建 JobDataMap 并放入唯一标识 id
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("id", id);

        String oldCron = ((CronTrigger) trigger).getCronExpression();
        // 判断cron是否相等,相等就不用改
        if (!cron.equals(oldCron)) {
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            CronTrigger newTrigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(jobName, group)
                    .usingJobData(jobDataMap)
                    .withSchedule(cronScheduleBuilder)
                    .build();

            Date date = scheduler.rescheduleJob(triggerKey, newTrigger);
            return date != null;
        } else {
            log.info("任务:{} CRON表达式没有变化", jobName);
        }

        return false;
    }

Job实现里,打印唯一标识,用于区分。如果你有业务需求,完全可以用这个唯一标识,去查询数据库,找出这个唯一标识所拥有的数据,然后做一系列操作。

/**
 * 简单任务1
 */
@Component
@Log4j2
public class SimpleTask1 implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) {
        log.info("{} 定时任务执行时间:{}", jobExecutionContext.getMergedJobDataMap().get("id"), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}

启动程序,可见两条记录各自的执行周期。

在这里插入图片描述

一个Job,各自运行,让博主想起黑塞 《悉达多》中的一句话:可是也有另一些人,一些为数不多的人,像沿着一条固定轨道运行的星星,没有风吹得到它们;它们有自身的规律和轨道。

总结

本文介绍了如何使用Quartz任务调度框架实现任务动态执行,参考下面这篇文章:

  • SpringBoot整合quartz完成定时任务执行配置热修改

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Scala_【1】概述

第一章 语言特点环境搭建(Windows)idea编写scalaHelloWorld注意事项 Scala是一门以Java虚拟机&#xff08;JVM&#xff09;为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言 语言特点 Scala是一门多范式的编程语言&#xff0c;Scala支持面向对象和函…

StableAnimator模型的部署:复旦微软提出可实现高质量和高保真的ID一致性人类视频生成

文章目录 一、项目介绍二、项目部署模型的权重下载提取目标图像的关节点图像&#xff08;这个可以先不看先用官方提供的数据集进行生成&#xff09;提取人脸&#xff08;这个也可以先不看&#xff09;进行图片的生成 三、模型部署报错 一、项目介绍 由复旦、微软、虎牙、CMU的…

最新高性能多目标优化算法:多目标麋鹿优化算法(MOEHO)求解LRMOP1-LRMOP6及工程应用---盘式制动器设计,提供完整MATLAB代码

一、麋鹿优化算法 麋鹿优化算法&#xff08;Elephant Herding Optimization&#xff0c;EHO&#xff09;是2024年提出的一种启发式优化算法&#xff0c;该算法的灵感来源于麋鹿群的繁殖过程&#xff0c;包括发情期和产犊期。在发情期&#xff0c;麋鹿群根据公麋鹿之间的争斗分…

螺杆支撑座在运用中会出现哪些问题?

螺杆支撑座是一种用于支撑滚珠螺杆的零件&#xff0c;通常用于机床、数控机床、自动化生产线等高精度机械设备中。在运用中可能会出现多种问题&#xff0c;这些问题源于多个方面&#xff0c;以下是对可能出现的问题简单了解下&#xff1a; 1、安装不当&#xff1a;安装过程中没…

Unity3d UGUI如何优雅的实现Web框架(Vue/Rect)类似数据绑定功能(含源码)

前言 Unity3d的UGUI系统与Web前端开发中常见的数据绑定和属性绑定机制有所不同。UGUI是一个相对简单和基础的UI系统&#xff0c;并不内置像Web前端&#xff08;例如 Vue.js或React中&#xff09;那样的双向数据绑定或自动更新UI的机制。UGUI是一种比较传统的 UI 系统&#xff…

从0入门自主空中机器人-2-2【无人机硬件选型-PX4篇】

1. 常用资料以及官方网站 无人机飞控PX4用户使用手册&#xff08;无人机基本设置、地面站使用教程、软硬件搭建等&#xff09;&#xff1a;https://docs.px4.io/main/en/ PX4固件开源地址&#xff1a;https://github.com/PX4/PX4-Autopilot 飞控硬件、数传模块、GPS、分电板等…

Windows上缺少xaudio2_9.dll是什么原因?

一、文件丢失问题&#xff1a;Windows上缺少xaudio2_9.dll是什么原因&#xff1f; xaudio2_9.dll是DirectX音频处理库的一个组件&#xff0c;它支持游戏中的音频处理功能。当你在Windows系统上运行某些游戏或音频软件时&#xff0c;如果系统提示缺少xaudio2_9.dll文件&#xf…

冥想的实践

这是我某一天的正念和冥想实践&#xff0c;我对正念练习、冥想练习进行了分别的统计。 正念练习&#xff1a;1分钟**5次 冥想&#xff1a;15分钟10分钟 正念练习&#xff0c;基本在工作休息时间练习。当然&#xff0c;工作过程中&#xff0c;也有一部分时间会有正念的状态&am…

一个简单的机器学习实战例程,使用Scikit-Learn库来完成一个常见的分类任务——**鸢尾花数据集(Iris Dataset)**的分类

机器学习实战通常是将理论与实践结合&#xff0c;通过实际的项目或案例&#xff0c;帮助你理解并应用各种机器学习算法。下面是一个简单的机器学习实战例程&#xff0c;使用Scikit-Learn库来完成一个常见的分类任务——**鸢尾花数据集&#xff08;Iris Dataset&#xff09;**的…

Redis 实战篇 ——《黑马点评》(上)

《引言》 在进行了前面关于 Redis 基础篇及其客户端的学习之后&#xff0c;开始着手进行实战篇的学习。因内容很多&#xff0c;所以将会分为【 上 中 下 】三篇记录学习的内容与在学习的过程中解决问题的方法。Redis 实战篇的内容我写的很详细&#xff0c;为了能写的更好也付出…

Intent--组件通信

组件通信1 获取子活动的返回值 创建Activity时实现自动注册&#xff01;【Activity必须要注册才能使用】 默认 LinearLayout 布局&#xff0c;注意 xml 中约束布局的使用&#xff1b; 若需要更改 线性布局 只需要将标签更改为 LinearLayout 即可&#xff0c;记得 设置线性布局…

word参考文献第二行缩进对齐

刚添加完参考文献的格式是这样&#xff1a; ”段落“—>缩进修改、取消孤行控制 就可以变成

UE(虚幻)学习(一) UE5.3.2和VS2022的安装以及遇到的问题和一些CS8604、CA2017报错问题.

最近工作很多东西是UE搞的&#xff0c;工作安排上也稍微缓口气&#xff0c;来学学UE&#xff0c;因为同事都用的UE5.3&#xff0c;所以就从UE5.3开始吧&#xff0c;之前学习过UE4&#xff0c;放上两年什么都不记得了。还是需要做一些记录。 本来安装不想写什么&#xff0c;谁知…

【YOLOv3】源码(train.py)

概述 主要模块分析 参数解析与初始化 功能&#xff1a;解析命令行参数&#xff0c;设置训练配置项目经理制定详细的施工计划和资源分配日志记录与监控 功能&#xff1a;初始化日志记录器&#xff0c;配置监控系统项目经理使用监控和记录工具&#xff0c;实时跟踪施工进度和质量…

systemverilog语法:assertion summary

topics assertion 介绍 Property在验证中的应用 ended表示sequence执行结束。 property 立即断言不消耗时间&#xff0c;好像if…else…&#xff0c;关键字不含property. 并发断言消耗时间&#xff0c;关键字含property. 立即断言 并发断言

blender中合并的模型,在threejs中显示多个mesh;blender多材质烘培成一个材质

描述&#xff1a;在blender中合并的模型导出为glb&#xff0c;在threejs中导入仍显示多个mesh&#xff0c;并不是统一的整体&#xff0c;导致需要整体高亮或者使用DragControls等不能统一控制。 原因&#xff1a;模型有多个材质&#xff0c;在blender中合并的时候&#xff0c;…

关于最新MySQL9.0.1版本zip自配(通用)版下载、安装、环境配置

一、下载 从MySQL官网进行下载MySQL最新版本&#xff0c;滑到页面最下面点击社区免费版&#xff0c;&#xff08;不是企业版&#xff09; 点击完成后选择自己想要下载的版本&#xff0c;选择下载zip压缩&#xff0c;不用debug和其他的东西。 下载完成后进入解压&#xff0c;注…

4.银河麒麟V10(ARM) 离线安装 MySQL

1. 系统版本 [rootga-sit-cssjgj-db-01u ~]# nkvers ############## Kylin Linux Version ################# Release: Kylin Linux Advanced Server release V10 (Lance)Kernel: 4.19.90-52.39.v2207.ky10.aarch64Build: Kylin Linux Advanced Server release V10 (SP3) /(La…

InfoNCE Loss详解(上)

引言 InfoNCE对比学习损失是学习句嵌入绕不开的知识点&#xff0c;本文就从头开始来探讨一下它是怎么来的。 先验知识 数学期望与大数定律 期望(expectation&#xff0c;expected value&#xff0c;数学期望&#xff0c;mathematical expectation)是随机变量的平均值&#…

机器人C++开源库The Robotics Library (RL)使用手册(一)

强大的、完整的C机器人开源库 1、是否可以免费商用&#xff1f;2、支持什么平台&#xff1f;3、下载地址4、开始&#xff01; 1、是否可以免费商用&#xff1f; Robotics Library&#xff08;RL&#xff09;是一个独立的C库&#xff0c;用于机器人运动学、运动规划和控制。它涵…