SchedulingConfigurer使用教程

SchedulingConfigurer使用教程:Java定时任务的高阶使用

在 Java 开发中,定时任务的管理和执行是一个常见需求。Spring 提供了多种方式来处理定时任务,其中 SchedulingConfigurer 是一个强大且灵活的接口,允许我们对定时任务进行更高级的配置和管理。本文将深入探讨如何使用 SchedulingConfigurer 实现复杂的定时任务调度。

1. 什么是 SchedulingConfigurer

SchedulingConfigurer 是 Spring Framework 提供的一个接口,用于配置任务调度。它允许我们自定义任务调度器的行为,例如配置线程池、定义任务调度规则等。

2. 配置 SchedulingConfigurer

首先,我们需要实现 SchedulingConfigurer 接口,并重写 configureTasks 方法。在这个方法中,我们可以使用 ScheduledTaskRegistrar 来注册我们的定时任务。

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

@Configuration
@EnableScheduling
public class CustomSchedulingConfigurer implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 这里可以添加自定义的任务调度逻辑
    }
}

3. 配置线程池

为了更好地管理和优化定时任务的执行,我们通常需要配置一个线程池。通过 ScheduledTaskRegistrar,我们可以很方便地设置一个自定义的线程池。

import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
@EnableScheduling
public class CustomSchedulingConfigurer implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.setThreadNamePrefix("Scheduled-Task-");
        taskScheduler.initialize();
        taskRegistrar.setTaskScheduler(taskScheduler);
    }
}

在上面的代码中,我们创建了一个 ThreadPoolTaskScheduler,并设置了线程池的大小和线程名称的前缀。然后,我们将这个任务调度器注册到 ScheduledTaskRegistrar 中。

4. 注册定时任务

configureTasks 方法中,我们可以使用 ScheduledTaskRegistrar 来注册我们的定时任务。我们可以使用 Cron 表达式来定义任务的执行时间。

import org.springframework.scheduling.support.CronTrigger;

@Configuration
@EnableScheduling
public class CustomSchedulingConfigurer implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.setThreadNamePrefix("Scheduled-Task-");
        taskScheduler.initialize();
        taskRegistrar.setTaskScheduler(taskScheduler);

        // 注册定时任务
        taskRegistrar.addTriggerTask(
            () -> System.out.println("定时任务执行: " + System.currentTimeMillis()),
            new CronTrigger("0/5 * * * * ?")
        );
    }
}

在上面的代码中,我们使用 addTriggerTask 方法注册了一个每 5 秒执行一次的定时任务。任务的逻辑可以是任何符合 Runnable 接口的代码。

5. 动态调整定时任务

有时候,我们需要在运行时动态调整定时任务的调度规则。我们可以通过维护一个共享变量来实现这一点。

import java.util.concurrent.atomic.AtomicReference;

@Configuration
@EnableScheduling
public class CustomSchedulingConfigurer implements SchedulingConfigurer {

    private final AtomicReference<String> cronExpression = new AtomicReference<>("0/5 * * * * ?");

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.setThreadNamePrefix("Scheduled-Task-");
        taskScheduler.initialize();
        taskRegistrar.setTaskScheduler(taskScheduler);

        // 注册定时任务
        taskRegistrar.addTriggerTask(
            () -> System.out.println("定时任务执行: " + System.currentTimeMillis()),
            triggerContext -> {
                String cron = cronExpression.get();
                return new CronTrigger(cron).nextExecutionTime(triggerContext);
            }
        );
    }

    // 动态修改 cron 表达式
    public void updateCronExpression(String newCron) {
        cronExpression.set(newCron);
    }
}

在上面的代码中,我们使用 AtomicReference 来保存 cron 表达式,并在 addTriggerTask 方法中动态获取最新的 cron 表达式。通过调用 updateCronExpression 方法,我们可以在运行时更新定时任务的执行规则。

6. 使用自定义的 Trigger 实现复杂的调度规则

除了使用 CronTrigger,我们还可以创建自定义的 Trigger 实现复杂的调度规则。例如,我们可以根据特定的业务逻辑动态计算下次执行时间。

import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;

import java.util.Date;

@Configuration
@EnableScheduling
public class CustomSchedulingConfigurer implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.setThreadNamePrefix("Scheduled-Task-");
        taskScheduler.initialize();
        taskRegistrar.setTaskScheduler(taskScheduler);

        // 注册定时任务
        taskRegistrar.addTriggerTask(
            () -> System.out.println("定时任务执行: " + System.currentTimeMillis()),
            new CustomTrigger()
        );
    }

    private static class CustomTrigger implements Trigger {
        @Override
        public Date nextExecutionTime(TriggerContext triggerContext) {
            // 自定义计算下一次执行时间的逻辑
            return new Date(System.currentTimeMillis() + 5000);
        }
    }
}

在上面的代码中,CustomTrigger 实现了 Trigger 接口,并在 nextExecutionTime 方法中自定义了下一次执行时间的计算逻辑。

7. 多任务调度和任务优先级

如果你的应用程序中有多个定时任务,并且这些任务有不同的优先级或需要不同的调度器,我们可以为每个任务配置不同的 TaskScheduler

@Configuration
@EnableScheduling
public class MultiTaskSchedulingConfigurer implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 配置任务1的调度器
        ThreadPoolTaskScheduler taskScheduler1 = new ThreadPoolTaskScheduler();
        taskScheduler1.setPoolSize(5);
        taskScheduler1.setThreadNamePrefix("Task1-");
        taskScheduler1.initialize();
        taskRegistrar.setTaskScheduler(taskScheduler1);
        taskRegistrar.addTriggerTask(
            () -> System.out.println("任务1执行: " + System.currentTimeMillis()),
            new CronTrigger("0/10 * * * * ?")
        );

        // 配置任务2的调度器
        ThreadPoolTaskScheduler taskScheduler2 = new ThreadPoolTaskScheduler();
        taskScheduler2.setPoolSize(3);
        taskScheduler2.setThreadNamePrefix("Task2-");
        taskScheduler2.initialize();
        taskRegistrar.addTriggerTask(
            () -> System.out.println("任务2执行: " + System.currentTimeMillis()),
            new CronTrigger("0/15 * * * * ?")
        );
    }
}

在上面的代码中,我们为两个任务配置了不同的 TaskScheduler,并且设置了不同的线程池和调度规则。

8. 动态添加和移除任务

在实际应用中,我们可能需要动态地添加和移除任务。我们可以通过维护一个任务列表来实现这一功能。

import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableScheduling
public class DynamicGroupedTaskSchedulingConfigurer implements SchedulingConfigurer {

    private final Map<TaskGroup, ThreadPoolTaskScheduler> taskSchedulers = new HashMap<>();
    private final Map<TaskGroup, List<ScheduledFuture<?>>> scheduledTasks = new HashMap<>();

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        configureTaskScheduler(TaskGroup.GROUP_A, 5, "GroupA-Task-");
        configureTaskScheduler(TaskGroup.GROUP_B, 3, "GroupB-Task-");
    }

    private void configureTaskScheduler(TaskGroup group, int poolSize, String threadNamePrefix) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(poolSize);
        taskScheduler.setThreadNamePrefix(threadNamePrefix);
        taskScheduler.initialize();
        taskSchedulers.put(group, taskScheduler);
        scheduledTasks.put(group, new ArrayList<>());
    }

    public void addTask(TaskGroup group, Runnable task, String cronExpression) {
        ThreadPoolTaskScheduler scheduler = taskSchedulers.get(group);
        if (scheduler != null) {
            ScheduledFuture<?> future = scheduler.schedule(task, new CronTrigger(cronExpression));
            scheduledTasks.get(group).add(future);
        }
    }

    public void removeAllTasks(TaskGroup group) {
        List<ScheduledFuture<?>> tasks = scheduledTasks.get(group);
        if (tasks != null) {
            for (ScheduledFuture<?> task : tasks) {
                task.cancel(true);
            }
            tasks.clear();
        }


    }
}

在上面的代码中,我们维护了一个任务列表 scheduledTasks,并提供了 addTaskremoveAllTasks 方法来动态地添加和移除任务。

示例使用

我们可以通过控制器或服务类来管理任务的添加和移除。

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/tasks")
public class TaskController {

    private final DynamicGroupedTaskSchedulingConfigurer taskConfigurer;

    public TaskController(DynamicGroupedTaskSchedulingConfigurer taskConfigurer) {
        this.taskConfigurer = taskConfigurer;
    }

    @PostMapping("/add")
    public String addTask(@RequestParam TaskGroup group, @RequestParam String cron) {
        taskConfigurer.addTask(group, () -> System.out.println("动态任务执行: " + System.currentTimeMillis()), cron);
        return "任务已添加";
    }

    @DeleteMapping("/removeAll")
    public String removeAllTasks(@RequestParam TaskGroup group) {
        taskConfigurer.removeAllTasks(group);
        return "所有任务已移除";
    }
}

通过上述控制器,我们可以动态地添加和移除任务组中的任务。例如,通过以下请求可以添加和移除任务:

  • 添加任务:POST /tasks/add?group=GROUP_A&cron=0/5 * * * * ?
  • 移除任务:DELETE /tasks/removeAll?group=GROUP_A

9. 错误处理和重试机制

在某些情况下,定时任务可能会失败。为了提高任务的可靠性,我们可以为定时任务添加错误处理和重试机制。

import org.springframework.scheduling.TriggerContext;

@Configuration
@EnableScheduling
public class ErrorHandlingSchedulingConfigurer implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.setThreadNamePrefix("ErrorHandling-Task-");
        taskScheduler.initialize();
        taskRegistrar.setTaskScheduler(taskScheduler);

        // 注册带错误处理和重试机制的定时任务
        taskRegistrar.addTriggerTask(
            () -> {
                try {
                    System.out.println("任务执行: " + System.currentTimeMillis());
                    // 模拟任务执行逻辑
                    if (Math.random() > 0.7) {
                        throw new RuntimeException("模拟任务执行失败");
                    }
                } catch (Exception e) {
                    System.err.println("任务执行失败: " + e.getMessage());
                    // 添加重试逻辑,例如延迟重试
                    taskScheduler.schedule(this::retryTask, new CronTrigger("0/10 * * * * ?"));
                }
            },
            new CronTrigger("0/5 * * * * ?")
        );
    }

    private void retryTask() {
        System.out.println("重试任务执行: " + System.currentTimeMillis());
    }
}

在上面的代码中,我们在任务执行逻辑中添加了错误处理逻辑,并在任务失败时使用 taskScheduler 调度重试任务。

10. 任务分组

任务分组可以帮助我们更好地组织和管理定时任务。我们可以为不同的任务分组设置不同的调度器和调度规则,从而实现更加灵活的任务管理。

10.1 定义任务组

我们可以通过一个枚举类来定义任务组,并在配置类中使用这些任务组来组织任务。

public enum TaskGroup {
    GROUP_A,
    GROUP_B
}

10.2 配置任务组调度器

为每个任务组配置不同的 TaskScheduler 和调度规则。

@Configuration
@EnableScheduling
public class GroupedTaskSchedulingConfigurer implements SchedulingConfigurer {

    private final Map<TaskGroup, ThreadPoolTaskScheduler> taskSchedulers = new HashMap<>();

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        configureTaskScheduler(TaskGroup.GROUP_A, 5, "GroupA-Task-");
        configureTaskScheduler(TaskGroup.GROUP_B, 3, "GroupB-Task-");

        // 注册任务组A的定时任务
        taskRegistrar.setTaskScheduler(taskSchedulers.get(TaskGroup.GROUP_A));
        taskRegistrar.addTriggerTask(
            () -> System.out.println("任务组A-任务1执行: " + System.currentTimeMillis()),
            new CronTrigger("0/10 * * * * ?")
        );

        // 注册任务组B的定时任务
        taskRegistrar.setTaskScheduler(taskSchedulers.get(TaskGroup.GROUP_B));
        taskRegistrar.addTriggerTask(
            () -> System.out.println("任务组B-任务1执行: " + System.currentTimeMillis()),
            new CronTrigger("0/15 * * * * ?")
        );
    }

    private void configureTaskScheduler(TaskGroup group, int poolSize, String threadNamePrefix) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(poolSize);
        taskScheduler.setThreadNamePrefix(threadNamePrefix);
        taskScheduler.initialize();
        taskSchedulers.put(group, taskScheduler);
    }
}

在上面的代码中,我们为每个任务组配置了不同的 TaskScheduler,并在 configureTasks 方法中注册了不同的定时任务。

10.3 动态添加和移除任务

在实际应用中,我们可能需要动态地添加和移除任务。我们可以通过维护一个任务列表来实现这一功能。

import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableScheduling
public class DynamicGroupedTaskSchedulingConfigurer implements SchedulingConfigurer {

    private final Map<TaskGroup, ThreadPoolTaskScheduler> taskSchedulers = new HashMap<>();
    private final Map<TaskGroup, List<ScheduledFuture<?>>> scheduledTasks = new HashMap<>();

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        configureTaskScheduler(TaskGroup.GROUP_A, 5, "GroupA-Task-");
        configureTaskScheduler(TaskGroup.GROUP_B, 3, "GroupB-Task-");
    }

    private void configureTaskScheduler(TaskGroup group, int poolSize, String threadNamePrefix) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(poolSize);
        taskScheduler.setThreadNamePrefix(threadNamePrefix);
        taskScheduler.initialize();
        taskSchedulers.put(group, taskScheduler);
        scheduledTasks.put(group, new ArrayList<>());
    }

    public void addTask(TaskGroup group, Runnable task, String cronExpression) {
        ThreadPoolTaskScheduler scheduler = taskSchedulers.get(group);
        if (scheduler != null) {
            ScheduledFuture<?> future = scheduler.schedule(task, new CronTrigger(cronExpression));
            scheduledTasks.get(group).add(future);
        }
    }

    public void removeAllTasks(TaskGroup group) {
        List<ScheduledFuture<?>> tasks = scheduledTasks.get(group);
        if (tasks != null) {
            for (ScheduledFuture<?> task : tasks) {
                task.cancel(true);
            }
            tasks.clear();
        }
    }
}

在上面的代码中,我们维护了一个任务列表 scheduledTasks,并提供了 addTaskremoveAllTasks 方法来动态地添加和移除任务。

示例使用

我们可以通过控制器或服务类来管理任务的添加和移除。

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/tasks")
public class TaskController {

    private final DynamicGroupedTaskSchedulingConfigurer taskConfigurer;

    public TaskController(DynamicGroupedTaskSchedulingConfigurer taskConfigurer) {
        this.taskConfigurer = taskConfigurer;
    }

    @PostMapping("/add")
    public String addTask(@RequestParam TaskGroup group, @RequestParam String cron) {
        taskConfigurer.addTask(group, () -> System.out.println("动态任务执行: " + System.currentTimeMillis()), cron);
        return "任务已添加";
    }

    @DeleteMapping("/removeAll")
    public String removeAllTasks(@RequestParam TaskGroup group) {
        taskConfigurer.removeAllTasks(group);
        return "所有任务已移除";
    }
}

通过上述控制器,我们可以动态地添加和移除任务组中的任务。例如,通过以下请求可以添加和移除任务:

  • 添加任务:POST /tasks/add?group=GROUP_A&cron=0/5 * * * * ?
  • 移除任务:DELETE /tasks/removeAll?group=GROUP_A

最佳实践

  1. 使用线程池优化性能:使用线程池可以避免线程资源耗尽,提升任务调度的性能和稳定性。
  2. 定期检查和调整任务调度:根据业务需求和系统性能,定期检查和调整定时任务的调度策略。
  3. 日志记录和监控:为定时任务添加日志记录和监控,方便排查问题和优化性能。
  4. 异常处理和重试机制:为关键任务添加异常处理和重试机制,确保任务的可靠执行。
  5. 分组管理任务:将相关任务进行分组管理,提高任务调度的灵活性和可维护性。

注意事项

  1. 避免任务堆积:确保任务执行时间短于调度周期,避免任务堆积导致系统性能下降。
  2. 合理设置线程池大小:根据实际业务需求和服务器性能,合理设置线程池的大小,避免线程资源耗尽或浪费。
  3. 避免重复调度:确保同一任务不会被重复调度,避免任务逻辑被多次执行。
  4. 监控系统资源:定时任务可能会消耗大量系统资源,需监控系统资源使用情况,防止资源耗尽。
  5. 优雅停机:在应用停机时,确保正在执行的任务能优雅终止或完成,避免数据不一致或任务中断。

通过遵循这些最佳实践和注意事项,你可以更加高效地管理和优化定时任务,确保系统的稳定性和可靠性。

结论

通过以上高级技巧,我们可以更加灵活和高效地管理定时任务。无论是自定义调度规则、多任务调度、动态启停任务,还是错误处理和重试机制,SchedulingConfigurer 都提供了丰富的扩展点。希望这些高级使用场景能够帮助你在实际项目中更好地应用定时任务调度。
如果你有其他高级使用技巧或遇到任何问题,欢迎在评论区分享和讨论。

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

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

相关文章

leetcode-每日一题

3101. 交替子数组计数https://leetcode.cn/problems/count-alternating-subarrays/ 给你一个 二进制数组 nums 。 如果一个 子数组 中 不存在 两个 相邻 元素的值 相同 的情况&#xff0c;我们称这样的子数组为 交替子数组 。 返回数组 nums 中交替子数组的数量。 示例 …

Linux字符设备驱动

一、字符设备驱动结构 1. cdev结构体 在Linux内核中&#xff0c;使用cdev结构体来描述一个字符设备 struct cdev {struct kobject kobj; //内嵌kobject对象struct module *owner; //所属的模块const struct file_operations *ops; //该设备的文件操作结构体struct list_head…

确认下单:购物车页面点击 去结算 按钮发起两个请求trade(显示购物车的商品信息和计算商品的总金额)findUserAddressList

文章目录 1、确认下单&#xff1a;购物车页面点击去结算1.1、在OrderController类中创建 trade 方法1.2、在CartController类中创建 checkedCartInfos1.3、CartServiceImpl 实现 checkedCartInfos的业务功能1.4、在service-cart-client模块下定义远程openFeign接口1.5、在SpzxO…

Java - 程序员面试笔记记录 实现 - Part3

4.1 线程与进程 线程是程序执行的最小单元&#xff0c;一个进程可以拥有多个线程&#xff0c;各个线程之间共享程序的内存空间以及一些进程级资源&#xff0c;但拥有自己的栈空间。 4.3 Java 多线程 方法一&#xff1a;继承 Thread 类&#xff0c;重写 run 方法&#xff1b;…

qt QGridLayout 简单实验1

1.概要 2.实验 2.1 实验1 简单实验跨行 2.1.1 代码 #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);~W…

Golang语法规范和风格指南(一)——简单指南

1. 前引 一个语言的规范的学习是重要的&#xff0c;直接关系到你的代码是否易于维护和理解&#xff0c;同时学习好对应的语言规范可以在前期学习阶段有效规避该语言语法和未知编程风格的冲突。 这里是 Google 提供的规范&#xff0c;有助于大家在开始学习阶段对 Golang 进行一…

如何魔改vnstat-docker项目使其支持每1分钟采样?

文章目录 一、概述二、官网参考1. 官网地址2. 查看打包过程3.打包命令 三、修改过的文件四、部署运行1. 编排文件2. 运行效果 一、概述 接前文 网络流量监控神器vnStat初探 我们已经了解了vnStat的作用、使用和docker部署。 同时也了解到官方版本支持的采样统计间隔最小为5分…

【Unity】unity学习扫盲知识点

1、建议检查下SystemInfo的引用。这个是什么 Unity的SystemInfo类提供了一种获取关于当前硬件和操作系统的信息的方法。这包括设备类型&#xff0c;操作系统&#xff0c;处理器&#xff0c;内存&#xff0c;显卡&#xff0c;支持的Unity特性等。使用SystemInfo类非常简单。它的…

Linux操作系统的引导过程

系统初始化进程与文件、systemd概述、单元类型、切换运行级别、查看系统默认默认运行、永久切换、常见的系统服务&#xff08;centos&#xff09;-CSDN博客 centos 7系统升级内核&#xff08;ELRepo仓库&#xff09;、小版本升级、自编译内核-CSDN博客 ss命令详细使用讲解文…

tongweb+ths6011测试websocket(by lqw)

本次使用的tongweb版本7049m4&#xff0c;测试包ws_example.war&#xff08;在tongweb安装目录的samples/websocket下&#xff09;&#xff0c;ths版本6011 首先在tongweb控制台部署一下ws_example.war,部署后测试是否能访问&#xff1a; 然後ths上的httpserver.conf的參考配…

游戏服务器搭建选VPS还是专用服务器?

游戏服务器搭建选VPS&#xff0c;VPS能够提供控制、性能和稳定性。它不仅仅是让游戏保持活力。它有助于减少延迟问题&#xff0c;增强您的游戏体验。 想象一下&#xff1a;你正沉浸在一场游戏中。 胜利在望。突然&#xff0c;屏幕卡住——服务器延迟。 很崩溃&#xff0c;对…

PageCache页缓存

一.PageCache基本结构 1.PageCache任务 PageCache负责使用系统调用向系统申请页的内存,给CentralCache分配大块儿的内存,以及合并前后页空闲的内存,整体也是一个单例,需要加锁. PageCache桶的下标按照页号进行映射,每个桶里span的页数即为下标大小. 2.基本结构 当每个线程的…

文件、文本阅读与重定向、路径与理解指令——linux指令学习(一)

前言&#xff1a;本节内容标题虽然为指令&#xff0c;但是并不只是讲指令&#xff0c; 更多的是和指令相关的一些原理性的东西。 如果友友只想要查一查某个指令的用法&#xff0c; 很抱歉&#xff0c; 本节不是那种带有字典性质的文章。但是如果友友是想要来学习的&#xff0c;…

Python 空间和时间高效的二项式系数(Space and time efficient Binomial Coefficient)

这里函数采用两个参数n和k&#xff0c;并返回二项式系数 C(n, k) 的值。 例子&#xff1a; 输入&#xff1a; n 4 和 k 2 输出&#xff1a; 6 解释&#xff1a; 4 C 2 等于 4!/(2!*2!) 6 输入&#xff1a; n 5 和 k 2 输出&#xff1a; 10 解释&#xff1a; 5 C …

moonlight+sunshine+ParsecVDisplay ipad8-windows 局域网串流

1.sunshine PC 安装 2.设置任意账户密码登录 3.setting 里 network启用UPNP IPV4IPV6 save apply 4.ParsecVDisplay虚拟显示器安装 5.ipad appstore download moonlight 6.以ipad 8 为例 2160*1620屏幕分辨率 7.ParsecVDisplay里面 custom设置2160*1620 240hz&#xff0c;…

python conda查看源,修改源

查看源 conda config --show-sources 修改源 可以直接vim .condarc修改源&#xff0c;

CSS中 实现四角边框效果

效果图 关键代码 border-radius:10rpx ;background: linear-gradient(#fff, #fff) left top,linear-gradient(#fff, #fff) left top,linear-gradient(#fff, #fff) right top,linear-gradient(#fff, #fff) right top,linear-gradient(#fff, #fff) left bottom,linear-gradient(…

CentOS7安装Mysql8.4.0

简介 本文介绍了Linux CentOS系统下Mysql8.4.0的下载和安装方法 环境 (rpm -q centos-release) centos-release-7-2.1511.el7.centos.2.10.x86_64 正文 一、去官网下载Mysql8.4.0 下载参考我另一篇mysql5.7.4的安装 CentOS7.9安装Mysql5.7-m14_centos下mysql5.7下载-CSDN博客…

flutter开发实战-Webview及dispose关闭背景音

flutter开发实战-Webview及dispose关闭背景音 当在使用webview的时候&#xff0c;dispose需要关闭网页的背景音或者音效。 一、webview的使用 在工程的pubspec.yaml中引入插件 webview_flutter: ^4.4.2webview_cookie_manager: ^2.0.6Webview的使用代码如下 初始化WebView…

AJAX-个人版-思路步骤整理版

前置知识&#xff1a;老式的web创建工程方法就是创建项目然后添加web工件&#xff0c;然后添加lib依赖如&#xff1a;tomcat,servlet&#xff0c;等。 传统请求 对于传统请求操作&#xff1a;整体流程也就是创建静态页面&#xff0c; <!DOCTYPE html> <html lang&q…