java常见的三种定时任务调度框架,写得太棒了

Kafka中时间轮分析与Java实现 https://www.cnblogs.com/softlin/p/7426083.html在Kafka中应用了大量的延迟操作但在Kafka中 并没用使用JDK自带的Timer或是DelayQueue用于延迟操作,而是使用自己开发的DelayedOperationPurgatory组件用于管理延迟操作,Kafka这类分布式框架有大量延迟操作并且对性能要求及其高,而java.util.Timer与java.util.concurrent.DelayQueue的插入和删除时间复杂度都为对数阶O(log n)并不 阅读详情

java定时任务目前主要有三种:

  1. Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行;而且作业类需要集成java.util.TimerTask,一般用的较少。

  2. Spring3.0以后自带的task,即:spring schedule,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。

  3. Quartz,这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行;代码稍显复杂。

定时器算法

1.小顶堆

堆,实际上是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于(或不小于)其左子节点和右子节点的值。

堆又分为两种,最大堆、最小堆。

  • 最大堆: 任一非叶子节点的值均大于其左子节点和右子节点的值。根节点的值是最大的。
  • 最小堆: 任一非叶子节点的值均小于其左子节点和右子节点的值。根节点的值是最小的。
小顶堆的实现方式

由于堆是一种经过排序的完全二叉树,因此在构建的时候需要对其新插入的节点进行一些操作以使其符合堆的性质。这种操作就叫上浮与下沉。

  • 上浮:将当前节点与其父节点相比,如果当前节点的值比父节点小,就把当前节点与父节点交换,然后继续前面的交换,直到当前节点比父节点的值大为止。上浮就是将符合条件的节点往上移的过程。

  • 下沉:将当前节点与其左、右子节点相比,如果当前节点的值比其中一个或两个子节点的值大,就把当前节点与两个子节点种比较小的那个交换,,然后继续前面的比较,直到当前节点的值比两个子节点的值都小为止。下沉就是将符合条件的节点往下移的过程。对于新手小白想更轻松的学好Java提升,Java架构,web开发、大数据,数据分析,人工智能等技术,这里给大家分享系统教学资源,扩列下我尉(同英):CGMX9880 【教程/工具/方法/解疑】

2.时间轮算法

见名知意,时间轮算法的数据结构类似于钟表上的数据指针,时间轮用环形数组的方式实现,数组中的每个元素都可以称之为槽(和redis集群的槽一样称呼)。槽的内部用双向链表存储着待执行的任务,添加和删除链表的操作时间复杂度为O(1),槽位本身也指代时间精度,比如一秒扫一个槽,那么这个时间轮的最高精度就是1秒。

当有一个延迟任务要插入时间轮时,首先计算其延迟时间与单位时间的余值,从指针指向的当前槽位移动余值的个数槽位,就是该延迟任务需要被放入的槽位。

举个例子,时间轮有8个槽位,编号为 0 ~ 7 。指针当前指向槽位 2 。新增一个延迟时间为 4 秒的延迟任务,4 % 8 = 4,因此该任务会被插入 4 + 2 = 6,也就是槽位6的延迟任务队列。

时间槽位的实现方式

时间轮的槽位实现可以采用循环数组的方式达成,也就是让指针在越过数组的边界后重新回到起始下标。概括来说,可以将时间轮的算法描述为:

用队列来存储延迟任务,同一个队列中的任务,其延迟时间相同。用循环数组的方式来存储元素,数组中的每一个元素都指向一个延迟任务队列。有一个当前指针指向数组中的某一个槽位,每间隔一个单位时间,指针就移动到下一个槽位。被指针指向的槽位的延迟队列,其中的延迟任务全部被触发。在时间轮中新增一个延迟任务,将其延迟时间除以单位时间得到的余值,从当前指针开始,移动余值对应个数的槽位,就是延迟任务被放入的槽位。

基于这样的数据结构,插入一个延迟任务的时间复杂度就下降到 O(1) 。而当指针指向到一个槽位时,该槽位连接的延迟任务队列中的延迟任务全部被触发。

延迟任务的触发和执行不应该影响指针向后移动的时间精确性。因此一般情况下,用于移动指针的线程只负责任务的触发,任务的执行交由其他的线程来完成。比如,可以将槽位上的延迟任务队列放入到额外的线程池中执行,然后在槽位上新建一个空白的新的延迟任务队列用于后续任务的添加。

代码实现

Timer

`/**
 * @className: TimerTest
 * @description: 测试java.util.Timer的定时器实现
 * @author: charon
 * @create: 2021-10-10 10:35
 */
public class TimerTest {
    public static void main(String[] args) {
        Timer timer = new Timer();
        // 延迟1s执行任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("延迟1s执行的任务"+new Date());
            }
        },1000);
        // 延迟3s执行任务,每隔5s执行一次
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("延迟3s每隔5s执行一次的任务"+new Date());
            }
        },3000,5000);
        // try {
        //     Thread.sleep(5000);
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // }
        // timer.cancel();
        // System.out.println("任务执行完毕"+new Date());
    }
}

延迟1s执行的任务Sun Oct 10 14:34:13 CST 2021
延迟3s执行的任务Sun Oct 10 14:34:15 CST 2021
延迟3s执行的任务Sun Oct 10 14:34:20 CST 2021
延迟3s执行的任务Sun Oct 10 14:34:25 CST 2021
延迟3s执行的任务Sun Oct 10 14:34:30 CST 2021` 

Timer的实现方式比较简单,其内部有两个主要的属性:

`/**
 * 用于存放定时任务TimeTask的列表
 */
private final TaskQueue queue = new TaskQueue();
/**
 * 用于执行定时任务的线程
 */
private final TimerThread thread = new TimerThread(queue);` 

TimerTask是一个实现了Runnable接口的抽象类。其run()方法用于提供具体的延时任务逻辑。

TaskQueue内部采用的是小顶堆的算法实现。根据任务的触发时间采用死循环的方式进行排序,将执行时间最小的任务放在前面。

`void add(TimerTask task) {
    // Grow backing store if necessary
    if (size + 1 == queue.length)
        queue = Arrays.copyOf(queue, 2*queue.length);

    queue[++size] = task;
    fixUp(size);
}

private void fixUp(int k) {
    while (k > 1) {
        int j = k >> 1;
        if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
            break;
        TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
        k = j;
    }
}` 

这样的方式就会有三个问题:

  1. 由于执行任务的线程只有一个,所以如果某个任务的执行时间过长,那么将破坏其他任务的定时精确性。如一个任务每1秒执行一次,而另一个任务执行一次需要5秒,那么如果是固定速率的任务,那么会在5秒这个任务执行完成后连续执行5次,而固定延迟的任务将丢失4次执行。对于新手小白想更轻松的学好Java提升,Java架构,web开发、大数据,数据分析,人工智能等技术,这里给大家分享系统教学资源,扩列下我尉(同英):CGMX9880 【教程/工具/方法/解疑】

  2. 如果执行某个任务过程中抛出了异常,那么执行线程将会终止,导致Timer中的其他任务也不能再执行。

  3. Timer使用的是绝对时间,即是某个时间点,所以它执行依赖系统的时间,如果系统时间修改了的话,将导致任务可能不会被执行。

由于Timer存在上面说的这些缺陷,在JDK1.5中,我们可以使用ScheduledThreadPoolExecutor来代替它,使用Executors.newScheduledThreadPool工厂方法或使用ScheduledThreadPoolExecutor的构造函数来创建定时任务,它是基于线程池的实现,不会存在Timer存在的上述问题,当线程数量为1时,它相当于Timer。

schedule

Spring Schedule在使用前都需要引入spring的包。

`
    org.springframework
    spring-core
    5.0.2.RELEASE


    org.springframework
    spring-context
    5.0.2.RELEASE


    org.springframework
    spring-beans
    5.0.2.RELEASE
` 

在这里我主要是使用spring boot注解的方式来实现:

`/**
 * 在spring boot的启动类上面添加 @EnableScheduling 注解
 */
@SpringBootApplication
@EnableScheduling
public class ScheduleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ScheduleApplication.class,args);
    }
}` 

新创建一个类,用来实现定时任务,这个类要注册成为Bean才行。

`/**
 * @className: ScheduleTest
 * @description: 测试schedule的执行器
 * @author: charon
 * @create: 2021-10-10 19:04
 */
@Component
public class ScheduleTest {
    /**
     * corn表达式:秒、分、时、日、月、星期
     *      值可以是数字,也可以是以下符号:
     *          *:所有值都匹配  示例: 0 0 * * * *:每小时(当秒和分都为0的时候)
     *          ?:只能用在日期和星期两个表达式种  示例: 0 0 12 * * ?   每天中午12点触发
     *          ,:或者  示例:0 0 9,13 * * *:每天的9点和13点
     *          /:增量值 示例: */10 * * * * *:每10秒
     *          -:区间  示例: 0 0/30 9-17 * * ? : 朝九晚五工作时间内每半小时
     */
    @Scheduled(cron="0 * * * * *")
    public void doSomething(){
        System.out.println("测试schedule的定时器,当秒为0的时候执行一次:"+new Date());
    }
}

测试schedule的定时器,当秒为0的时候执行一次:Sun Oct 10 19:18:00 CST 2021
测试schedule的定时器,当秒为0的时候执行一次:Sun Oct 10 19:19:00 CST 2021
测试schedule的定时器,当秒为0的时候执行一次:Sun Oct 10 19:20:00 CST 2021` 

@Scheduled注解的另外两个重要属性:fixedRate和fixedDelay

  • fixedDelay:上一个任务结束后多久执行下一个任务

  • fixedRate:上一个任务的开始到下一个任务开始时间的间隔

`/**
 * 测试fixedRate,每2s执行一次
 * @throws Exception
 */
@Scheduled(fixedRate = 2000)
public void fixedRate() throws Exception {
    System.out.println("fixedRate开始执行时间:" + new Date(System.currentTimeMillis()));
    //休眠1秒
    Thread.sleep(1000);
    System.out.println("fixedRate执行结束时间:" + new Date(System.currentTimeMillis()));
}

fixedRate开始执行时间:Sun Oct 10 19:59:05 CST 2021
fixedRate执行结束时间:Sun Oct 10 19:59:06 CST 2021
fixedRate开始执行时间:Sun Oct 10 19:59:07 CST 2021
fixedRate执行结束时间:Sun Oct 10 19:59:08 CST 2021
fixedRate开始执行时间:Sun Oct 10 19:59:09 CST 2021
fixedRate执行结束时间:Sun Oct 10 19:59:10 CST 2021

/**
 * 等上一次执行完等待1s执行
 * @throws Exception
 */
@Scheduled(fixedDelay = 1000)
public void fixedDelay() throws Exception {
    System.out.println("fixedDelay开始执行时间:" + new Date(System.currentTimeMillis()));
    //休眠两秒
    Thread.sleep(1000 * 2);
    System.out.println("fixedDelay执行结束时间:" + new Date(System.currentTimeMillis()));
}

fixedDelay执行结束时间:Sun Oct 10 20:07:23 CST 2021
fixedDelay开始执行时间:Sun Oct 10 20:07:24 CST 2021
fixedDelay执行结束时间:Sun Oct 10 20:07:26 CST 2021
fixedDelay开始执行时间:Sun Oct 10 20:07:27 CST 2021
fixedDelay执行结束时间:Sun Oct 10 20:07:29 CST 2021` 

如果是强调任务间隔的定时任务,建议使用fixedRate和fixedDelay,如果是强调任务在某时某分某刻执行的定时任务,建议使用cron表达式。

Spring Schedule的Corn是使用的时间轮算法(分层时间轮,每个时间粒度对应一个时间轮,多个时间轮时间进行级联协调)。

在CronSequenceGenerator.java这个类中,对每个CornTask都维护了一下7个Bitset(使用位数组而不用list,set之类的数据结构,一方面是因为空间效率,更重要的是接下来的操作主要是判断某个值是否匹配和从某个值开始找最近的下一个能够匹配的值)

`private final BitSet months = new BitSet(12);

private final BitSet daysOfMonth = new BitSet(31);

private final BitSet daysOfWeek = new BitSet(7);

private final BitSet hours = new BitSet(24);

private final BitSet minutes = new BitSet(60);

private final BitSet seconds = new BitSet(60);` 

然后根据配置的corn值计算这个任务对应的值计算每个bit的值。如我这里配置的每分钟执行一次的CornTask的结果如下:

图片

CronSequenceGenerator负责解析用户配置的Cron表达式,并提供next方法,根据给定的时间获取符合cron表达式规则的最近的下一个时间。CronTrigger实现Trigger的nextExecutionTime方法,根据定时任务执行的上下文环境(最近调度时间和最近完成时间)决定查找下一次执行时间的左边界,之后调用CronSequenceGenerator的next方法从左边界开始找下一次的执行时间。

CronSequenceGenerator的doNext算法从指定时间开始(包括指定时间)查找符合cron表达式规则下一个匹配的时间。如图3-4所示,其整体思路是:沿着秒→分→时→日→月逐步检查指定时间的值。如果所有域上的值都已经符合规则那么指定时间符合cron表达式,算法结束。否则,必然有某个域的值不符合规则,调整该域到下一个符合规则的值(可能调整更高的域),并将较低域的值调整到最小值,然后从秒开始重新检查和调整。

`private void doNext(Calendar calendar, int dot) {
    List resets = new ArrayList<>();

    int second = calendar.get(Calendar.SECOND);
    List emptyList = Collections.emptyList();
    int updateSecond = findNext(this.seconds, second, calendar, Calendar.SECOND, Calendar.MINUTE, emptyList);
    if (second == updateSecond) {
        resets.add(Calendar.SECOND);
    }

    int minute = calendar.get(Calendar.MINUTE);
    int updateMinute = findNext(this.minutes, minute, calendar, Calendar.MINUTE, Calendar.HOUR_OF_DAY, resets);
    if (minute == updateMinute) {
        resets.add(Calendar.MINUTE);
    }
    else {
        doNext(calendar, dot);
    }

    int hour = calendar.get(Calendar.HOUR_OF_DAY);
    int updateHour = findNext(this.hours, hour, calendar, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_WEEK, resets);
    if (hour == updateHour) {
        resets.add(Calendar.HOUR_OF_DAY);
    }
    else {
        doNext(calendar, dot);
    }

    int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
    int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
    int updateDayOfMonth = findNextDay(calendar, this.daysOfMonth, dayOfMonth, this.daysOfWeek, dayOfWeek, resets);
    if (dayOfMonth == updateDayOfMonth) {
        resets.add(Calendar.DAY_OF_MONTH);
    }
    else {
        doNext(calendar, dot);
    }

    int month = calendar.get(Calendar.MONTH);
    int updateMonth = findNext(this.months, month, calendar, Calendar.MONTH, Calendar.YEAR, resets);
    if (month != updateMonth) {
        if (calendar.get(Calendar.YEAR) - dot > 4) {
            throw new IllegalArgumentException("Invalid cron expression \"" + this.expression +
                                               "\" led to runaway search for next trigger");
        }
        doNext(calendar, dot);
    }

}

private int findNext(BitSet bits, int value, Calendar calendar, int field, int nextField, List lowerOrders) {
    int nextValue = bits.nextSetBit(value);
    // 下一个匹配值是-1,则将对更高的域做加1操作,从0开始查找下一个匹配值,将当前域设置为下一个匹配值,重置比当前域低的所有域设置为最小值,递归调度本算法。
    if (nextValue == -1) {
        calendar.add(nextField, 1);
        reset(calendar, Collections.singletonList(field));
        nextValue = bits.nextSetBit(0);
    }
    // 下一个匹配值不是当前值但也不是-1,则将当前域设置为下一个匹配值,将比当前域低的所有域设置为最小值,递归调度本算法
    if (nextValue != value) {
        calendar.set(field, nextValue);
        reset(calendar, lowerOrders);
    }
    // 下一个匹配值就是当前值,则匹配通过,如果当前域是月则算法结束,否则继续处理下一个更高的域。
    return nextValue;
}` 

Quartz

在这里还是使用Spring Boot 集成Quartz;

引入依赖:

`
     org.quartz-scheduler
     quartz
     2.3.0
` 

测试的job业务处理类:

`/**
 * @className: QuartzJob
 * @description: 业务逻辑处理类
 * @author: charon
 * @create: 2021-10-11 14:32
 */
public class QuartzJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("执行quartz定时器开始:" + new Date());
        // 模拟业务逻辑
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行quartz定时器结束:" + new Date());
    }
}` 

实例化Job,将任务触发器加入任务调度中:

`/**
 * @className: QuartzConfig
 * @description: scheduler的启动、结束等控制类
 * @author: charon
 * @create: 2021-10-11 14:38
 */
@Configuration
public class QuartzConfig {

    @Autowired
    private Scheduler scheduler;

    /**
     * 开始定时器
     */
    public void startJob() throws SchedulerException {
        // 通过JobBuilder构建JobDetail实例,JobDetail规定只能是实现Job接口的实例
        // JobDetail 是具体Job实例
        JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class).withIdentity("job", "group").build();
        // 基于表达式构建触发器   每5秒种执行一次
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
        // CronTrigger表达式触发器 继承于Trigger
        // TriggerBuilder 用于构建触发器实例
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("job1", "group1")
                .withSchedule(cronScheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }

    /**
     * 删除某个任务
     *
     * @param name job的名称
     * @param group job的分组
     * @throws SchedulerException
     */
    public void deleteJob(String name, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(name, group);
        if (scheduler.checkExists(jobKey)){
            scheduler.deleteJob(jobKey);
        }
    }` 

测试类(spring容器初始化完成后执行):

`/**
 * @className: QuartzTest
 * @description: 测试quartz的定时器
 * @author: charon
 * @create: 2021-10-11 10:33
 */
@Configuration
public class QuartzTest implements ApplicationListener {

    @Autowired
    private QuartzConfig quartzConfig;

    /**
     * 监听初始化quartz
     * @param event
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("容器初始化完成");
        try {
            quartzConfig.startJob();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

容器初始化完成
2021-10-11 15:02:46.947  INFO 19628 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 7010 (http) with context path ''
2021-10-11 15:02:49.558  INFO 19628 --- [           main] c.c.ScheduleApplication  : Started ScheduleApplication in 10.734 seconds (JVM running for 11.644)
执行quartz定时器开始:Mon Oct 11 15:02:50 CST 2021
执行quartz定时器结束:Mon Oct 11 15:02:52 CST 2021
执行quartz定时器开始:Mon Oct 11 15:02:55 CST 2021
执行quartz定时器结束:Mon Oct 11 15:02:57 CST 2021
执行quartz定时器开始:Mon Oct 11 15:03:00 CST 2021
执行quartz定时器结束:Mon Oct 11 15:03:02 CST 2021` 
一文搞懂,java中常用的定时任务框架-单体 一、阅读收获 ✔️1. 了解常用的单体应用定时任务框架 ✔️2. 掌握定时任务在单体中如何使用 二、Timer+TimerTask 这是jdk自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。 使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行,一般用的较少。 /** * @Description: 1. Timer+TimerTask:(jdk自带) * 这是java自带的java.util.Timer类,这个类允许.. 阅读详情

相关推荐

java常见的三种定时任务调度框架介绍

java常见的三种定时任务调度框架介绍

h123_58的博客 1992

Java -- 定时任务实现方式

定义Job有两种方式,@Servicetry {}}}第二种是继承,重写方法,这种方式可以接受JobDetail传递的参数@Overridetry {}// 获取参数System.out.println("参数:" + date);}}这样就把JobDetail和我们之前定义的QuartzJob关联起来了。...

MinggeQingchun的博客 3万+

spring框架实现定时任务调度

应用场景 一般用于定时发工资 或者定时清理垃圾 周期性更新数据库 使用 先导入包 第一步: 在Spring配置文件中进行配置基本信息 第二步:在xsi中添加 http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd 第三步:然后是必须的一项,因为这个是spring提供的定时任务,所以需要进行扫描类下的包才能执行相应的操作 配置扫描的包 我把相应的测

小乌龟 3740

Java定时任务汇总

最全定时任务总结

u012154840的博客 1万+

Java定时任务调度详解

前言 在实际项目开发中,除了Web应用、SOA服务外,还有一类不可缺少的,那就是定时任务调度。定时任务的场景可以说非常广泛,比如某些视频网站,购买会员后,每天会给会员送成长值,每月会给会员送一些电影券;比如在保证最终一致性的场景中,往往利用定时任务调度进行一些比对工作;比如一些定时需要生成的报表、邮件;比如一些需要定时清理数据的任务等。本篇博客将系统的介绍定时任务调度,会涵盖Timer、ScheduledExecutorService、开源工具包Quartz,以及Spring和Quartz的结合等内容。

m0_62714732的博客 2512

任务调度框架-如何实现定时任务+RabbitMQ事务+手动ACK

任务调度框架-如何实现定时任务+RabbitMQ事务+手动ACK

Sky_MaoXiaoqi的博客 2009

Java定时任务调度框架

Java定时任务调度框架1. 定时任务调度概述1.1 业务场景概述1.2 任务调度场景1.3 调度任务分类2. 定时任务调度详解2.1 Timer2.2 ScheduledExecutorService2.3 SpringTask2.4 Quartz2.5 xxl-job2.6 elastic-job 1. 定时任务调度概述 1.1 业务场景概述 日常开发中定时任务使用场景很多,比如支付系统每天凌晨定时清算对账;未支付订单超时回收;商品定时上下架等等。定时任务框架选择有很多,面对不同的业务场景可以选择不同的

靖节先生的博客 1万+

Spring Boot框架在Java领域的定时任务调度优化_副本2

本文旨在为Java开发者提供Spring Boot框架下定时任务调度的全面优化指南。我们将覆盖从基础配置到高级优化的完整知识体系,特别关注高并发、分布式环境下的调度挑战。文章首先介绍定时任务的基本概念,然后深入Spring Boot的两种主要调度方式,接着探讨优化策略,最后展示分布式环境下的解决方案。Cron表达式:用于配置定时任务执行时间的字符串格式任务调度器(Scheduler):负责管理和执行定时任务的组件任务触发器(Trigger):定义任务执行条件的组件任务持久化。

AI开发架构师 970

Java定时任务及常见框架

Java定时任务及常见框架

weixin_45734473的博客 3791

【2.1 分布式定时任务(XXL-JOB)】一文汇总所有常见的Java定时任务框架介绍

在Java中,常见的定时任务框架包括Quartz、Spring Task、Elastic-Job和XXL-JOB等。

奶爸的编程之路,也就一周冷个三天 1635

使用 NTLite 来定制专属的官方 windows 镜像

移除掉不想要的一些自带的应用,比如 Xbox,office 啥的,这个就根据自己的实际需求来选择,备注基本都是有解释这个应用的作用。win 11 的右键展示不喜欢,更喜欢 win10 的,但是需要修改注册表,这里直接把下面的命令写到后期任务里面。NTLite 的自动保存会话功能会保留操作过的配置,是一个 xml 文件,可以在下次升级镜像版本的时候,导入配置。关闭 windows defender,不关会有这个提示,点是,就会跳到对应的界面。勾选创建 iso,配置 iso 镜像的名字,然后点开始。

以梦为马|越骑越傻 3334

java如何实现定时任务_Java定时任务的三种实现方式

前言现代的应用程序早已不是以前的那些由简单的增删改查拼凑而成的程序了,高复杂性早已是标配,而任务的定时调度与执行也是对程序的基本要求了。很多业务需求的实现都离不开定时任务,例如,每月一号,移动将清空你上月未用完流量,重置套餐流量,以及备忘录提醒、闹钟等功能。java 系统中主要有三种方式来实现定时任务:Timer和TimerTaskScheduledExecutorService三方框架 Quar...

weixin_32862641的博客 1万+

一文带你了解SOA接口测试

了解汽车电子行业的人,近几年可能经常见到一个词“SOA”,那SOA具体是什么?作为测试人员应该要测试哪些内容呢?这篇文章就简单给大家介绍一下。SOA(Service Oriented Architecture)是一种面向服务的架构,最早应用于IT行业,虽然行业内对于SOA的定义没有一个统一的描述,但是总体而言,SOA把功能定义成为服务,服务带有明确的可调用接口,并可以通过网络调用。在汽车领域,对于高性能计算平台而言,为了实现域控制器以及区域控制器之间的互联互通和软硬分离、缩短开发周期等优势,也引入了SOA的

怿星科技的博客 7156

如何在Java中实现定时任务调度?

大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨在Java应用中如何实现定时任务调度。定时任务是许多应用程序中常见的需求,它可以用于周期性地执行特定的任务或在特定时间点执行一次性任务。在Java中,有多种方式可以实现定时任务调度,我们将介绍几种常用的方法和技术。

微赚开发者技术分享博客 1133

网络抓包实战:如何捕获与解析802.1Q VLAN标签报文

在网络通信中,数据包是信息传输的基本单元,而VLAN(虚拟局域网)技术通过在数据链路层插入802.1Q标签,实现了网络流量的逻辑隔离与高效管理。其核心原理是在标准以太网帧头中嵌入4字节的VLAN标识,使交换机能够根据VID(VLAN ID)进行精准转发。掌握VLAN报文的捕获与解析技术,对于网络故障排查、安全审计和性能优化具有重要价值,尤其在需要验证网络隔离策略或诊断Trunk链路异常时。本文聚焦于使用Wireshark工具,结合端口镜像等实操方法,详细演示如何抓取并解读携带802.1Q标签的原始报文,帮助

weixin_33417488的博客 383

RapidScada在windows下的安装(详细)

1.下载地址:RapidScada官网 2.版本说明 有Linux版本和Windows版本,看自己的需求下载,推荐下载最新稳定版本。 3.系统要求 4.安装前的准备(windows10) 首先打开Windows功能: 先关闭 Microsoft .NET Framework3.5的 Windows Communication Foundation 子组件。如下图: Microsoft .NET Framework配置按下图勾选: iis安装的功能: 5.安装 解压..

m0_51275803的博客 4263

P8大佬透彻讲解java定时任务调度框架

java定时任务目前主要有三种: Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行;而且作业类需要集成java.util.TimerTask,一般用的较少。 Spring3.0以后自带的task,即:spring schedule,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。 Quartz,这是一个功能比较强大的的调度器,可以让你的程序在指定时间执

weixin_48791363的博客 2613

【华为OD机试真题】65、高矮个子排队 | 机试真题+思路参考+代码解析 (C++、Java、Py、C语言、JS)

题目描述 >现在有一队小朋友,他们高矮不同,我们以正整数数组表示这一队小朋友的身高,如数组{5,3,1,2,3}。 >  我们现在希望小朋友排队,以"高" "矮" "高" "矮" 顺序排列,每一个高位置的小朋友要比相邻的位置高或者相等;每个 "矮" 位置的小朋友要比相邻的位置矮或者相等; >  要求小朋友们移动的距离和最小,第一个从“高"位开始排,输出最小移动距离即可。 >  例如,在示范小队{5,3,1,2,3}中, {5, 1, 3, 2, 3}是排序结果。 > 

KJ.JK 2100

Java定时任务框架整理

再见 Spring Task,这个定时任务框架真香! 目录 再见 Spring Task,这个定时任务框架真香! 为什么需要定时任务? 单机定时任务技术选型 Timer ScheduledExecutorService Spring Task 时间轮 分布式定时任务技术选型 Quartz Elastic-Job XXL-JOB PowerJob 总结 以下文章来源于JavaGuide,作者Guide哥 JavaGuide. 专注Java后端学习和大厂面试的公众号!开源.

AVATAR 6435

华为OD技术面八股_C++_05

汇总华为OD技术面C++方向技术面八股文,提供详细解答。

qq_45776114的博客 413

ModuleNotFoundError: No module named‘langchain.schema.output parserlangchain.schema is not a package

电脑里现有的langchain库版本与import所需的不同,卸载原有langchain,重新下载最新的langchain。在步骤1后,若仍不可运行。在确保已重新下载新langchain后,重启python后,可正常运行。在版本号中填入所需的langchain版本号,可在requirements.txt查看。运行如下代码时,出现error。

weixin_45751925的博客 1713

分布式定时任务调度框架

分布式任务调度框架几乎是每个大型应用必备的工具,本文介绍了任务调度框架使用的需求背景和痛点,对业界普遍使用的开源分布式任务调度框架的使用进行了探究实践,并分析了这几种框架的优劣势和对自身业务的思考。 一、业务背景 1.1 为什么需要使用定时任务调度 (1)时间驱动处理场景:整点发送优惠券,每天更新收益,每天刷新标签数据和人群数据。 (2)批量处理数据:按月批量统计报表数据,批量更新短信状态,实时性要求不高。 (3)异步执行解耦:活动状态刷新,异步执行离线查询,与内部逻辑解耦。 1.2 使.

winrh的博客 1991

推荐系统中召回率Recall计算方式附代码

Recall(召回率)是指在所有实际有交互的用户 - 物品对中,推荐系统成功预测出的比例。具体来说,设所有有交互的用户 - 物品对为S,推荐系统预测出的用户 - 物品对为T,则Recall的计算公式为:

优快云 精品推荐 1842
上一篇: Java反编译工具,你知道几个?
下一篇: 2022年最值得学习的5款开源Java框架
python88889
博客等级 码龄5年 15粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值