【线程池】ScheduledExecutorService接口和ScheduledThreadPoolExecutor定时任务线程池使用详解

news2024/11/15 23:48:27

目录

〇、简介

0.1 ScheduledExecutorService 和 Timer 的区别

一、什么是ScheduledExecutorService?

二、ScheduledThreadPoolExecutor中的方法

2.1 构造方法

2.2 schedule方法

2.3 scheduleAtFixedRate方法

2.4 scheduleWithFixedDelay方法

2.5 setContinueExistingPeriodicTasksAfterShutdownPolicy方法

2.6 setExecuteExistingDelayedTasksAfterShutdownPolicy方法

2.7 scheduleAtFixedRate和scheduleWithFixedDelay的区别

三、练习

3.1 schedule练习1

3.2 schedule练习2

3.3 scheduleAtFixedRate练习1

3.4 scheduleAtFixedRate练习2

3.5 scheduleWithFixedDelay练习

3.6 setContinueExistingPeriodicTasksAfterShutdownPolicy练习


〇、简介

在实现定时调度功能的时候,我们往往会借助于第三方类库来完成,比如:quartz、Spring Schedule等等。JDK从1.3版本开始,就提供了基于Timer的定时调度功能。在Timer中,任务的执行是串行的。这种特性在保证了线程安全的情况下,往往带来了一些严重的副作用,比如任务间相互影响、任务执行效率低下等问题。为了解决Timer的这些问题,JDK从1.5版本开始,提供了基于ScheduledExecutorService的定时调度功能。

虽然Java的定时调度可以通过Timer&TimerTask来实现。但是由于其实现的方式为单线程,因此从JDK1.3发布之后就一直存在一些问题,大致如下:

  • 多个任务之间会相互影响
  • 多个任务的执行是串行的,性能较低

ScheduledExecutorService在设计之初就是为了解决Timer&TimerTask的这些问题。因为天生就是基于多线程机制,所以任务之间不会相互影响(只要线程数足够。当线程数不足时,有些任务会复用同一个线程)。

除此之外,因为其内部使用的延迟队列,本身就是基于等待/唤醒机制实现的,所以CPU并不会一直繁忙。同时,多线程带来的CPU资源复用也能极大地提升性能。

0.1 ScheduledExecutorService Timer 的区别

  • Timer 对系统时钟的变化敏感,ScheduledThreadPoolExecutor不是;
  • Timer 内部只有一个执行线程,因此长时间运行的任务会延迟其他任务,而且如果有多个任务的话就会顺序执行,这样我们的延迟时间和循环时间就会出现问题。 ScheduledThreadPoolExecutor 可以配置任意数量的线程。 此外,如果你想(通过提供 ThreadFactory),你可以完全控制创建的线程。所以在多线程环境下对延迟任务和循环任务要求严格的时候,就需要考虑使用ScheduledExecutorService了;
  • 在TimerTask 中抛出的运行时异常会杀死一个线程,从而导致 Timer 死机,即计划任务将不再运行。ScheduledThreadExecutor 不仅捕获运行时异常,还允许您在需要时处理它们(通过重写 afterExecute 方法ThreadPoolExecutor)。抛出异常的任务将被取消,但其他任务将继续运行。

综上,在 JDK1.5 之后,你没有理由再使用 Timer 进行任务调度了。当然,在实际项目中基本也不会用到ScheduledThreadPoolExecutor,所以对这部分大家只需要简单了解一下它的思想。

备注: Quartz 是一个由 Java 编写的任务调度库,由 OpenSymphony 组织开源出来。在实际项目开发中使用 Quartz 的还是居多,比较推荐使用 Quartz。因为 Quartz 理论上能够同时对上万个任务进行调度,拥有丰富的功能特性,包括任务调度、任务持久化、可集群化、插件等等。

一、什么是ScheduledExecutorService

ScheduledExecutorService接口是基于ExecutorService的功能实现的延迟和周期执行任务的功能。每个任务以及每个任务的每个周期都会提交到线程池中由线程去执行,所以任务在不同周期内执行它的线程可能是不同的。ScheduledExecutorService接口的默认实现类是ScheduledThreadPoolExecutor。在周期执行的任务中,如果任务执行时间大于周期时间,则会以任务时间优先,等任务执行完毕后才会进入下一次周期

ScheduledExecutorService接口中定义了一些方法:

public interface ScheduledExecutorService extends ExecutorService {
    // 在指定延时后执行一次
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);

    // 在指定延时后执行一次
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);
                                           
    // 在指定延时后开始执行,并在之后以指定时间间隔重复执行(间隔不包含任务执行的时间)
    // 相当于之后的延时以任务开始计算
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);

    // 在指定延时后开始执行,并在之后以指定延时重复执行(间隔包含任务执行的时间)
    // 相当于之后的延时以任务结束计算
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);
}

二、ScheduledThreadPoolExecutor中的方法

之前我们讲的ThreadPoolExecutor是java的普通线程池。而ScheduledThreadPoolExecutor是java提供的定时任务线程池。

因为ScheduledThreadPoolExecutor类继承了ThreadPoolExecutor类,所以有很多方法都是来自ThreadPoolExecutor类,其本身是支持线程池的所有功能,它自己还额外提供了一些关于延迟执行和定时任务的方法。我们常用的有如下几种:

/**
 * 带延迟时间的调度,只执行一次
 * 调度之后可通过Future.get()阻塞直至任务执行完毕
 */
1. public ScheduledFuture<?> schedule(Runnable command,
                                      long delay, TimeUnit unit);
/**
 * 带延迟时间的调度,只执行一次
 * 调度之后可通过Future.get()阻塞直至任务执行完毕,并且可以获取执行结果
 */
2. public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                          long delay, TimeUnit unit);
/**
 * 带延迟时间的调度,循环执行,固定频率
 */
3. public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                 long initialDelay,
                                                 long period,
                                                 TimeUnit unit);
/**
 * 带延迟时间的调度,循环执行,固定延迟
 */
4. public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                    long initialDelay,
                                                    long delay,
                                                    TimeUnit unit);

2.1 构造方法

ScheduledThreadPoolExecutor(int corePoolSize)
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory)
ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler)
ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler)
  • corePoolSize:线程池核心线程数
  • threadFactory:线程工厂
  • handler:任务拒绝策略

构造一个Schedule线程池,最大线程数为Integer的最大值,线程的空闲时间为0,队列采用的是DelayedWorkQueue。

2.2 schedule方法

// 延迟执行无返回值单个任务,该方法返回ScheduledFuture,就可以理解普通线程池中返回的Future
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
// 延迟执行有返回值单个任务,该方法返回ScheduledFuture
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
  • command:要延迟执行的任务(Runnable / Callable
  • delay:延时的时间
  • unit:时间单位

延时delay时长执行Runnable或者Callable任务,只会执行一次。执行Runnable任务时是没有结果返回的,那为什么还会返回ScheduledFuture,因为我们可以通过Future做一些取消任务等操作。该方法会使任务在delay时间之后去执行。

调度之后还可以通过Future.get()阻塞直至任务执行完毕。

2.3 scheduleAtFixedRate方法

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
  • command:Runnable任务
  • initialDelay:任务首次执行前的延迟时间
  • period:周期时间
  • unit:时间单位

固定周期性执行任务,当任务的执行时长大于周期,那么下一个周期任务将在上一个执行完毕之后马上执行。

作用:指定的延迟时间( initialDelay)调度第一次,后续以 period为一个时间周期进行调度,该方法并不 care 每次任务执行的耗时,如果某次耗时超过调度周期(period),则下一次调度从上一次任务结束时开始,然后接着按照period的间隔严格执行任务。

也就是说这个方法会严格按照周期间隔去执行,并不会管任务的执行时间。

任务的第一次会在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。意思是下一次执行任务的时间与任务执行过程花费的时间无关,只与period有关!

如果此任务的任何一个执行要花费比其周期更长的时间,则将推迟后续执行,但不会同时执行。

如果任务的任何一个执行遇到异常,则后续执行都会被取消。否则,只能通过执行程序的取消或终止方法来终止该任务。

2.4 scheduleWithFixedDelay方法

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
  • command:Runnable任务
  • initialDelay:任务首次执行前的延迟时间
  • delay:延时时间
  • unit:时间单位

固定延时执行任务,也是周期性任务,和scheduleAtFixedRate不同的是:scheduleAtFixedRate当任务执行时间小于周期时间时,此时周期时间到了的时候会进入下一周期,这一点和scheduleWithFixedDelay是没有区别的;但是如果任务执行时间大于周期时间时,scheduleAtFixedRate的任务结束后会立即进入下一周期;而scheduleWithFixedDelay是无论你任务时间是否超过,都将会在你任务执行完毕后延迟固定时间(delay),才会进入下一周期。

作用:在指定的延迟时间( delay)调度第一次,后续以 period 为一个时间周期进行调度,该方法非常 care 上一次任务执行的耗时,如果某次耗时超过调度周期(period),则下一次调度时间为 上一次任务结束时间 + 调度周期时间

也就是说使用scheduleWithFixedDelay可以实现在每一次执行终止和下一次执行开始之间都存在给定的延迟(delay)。

2.5 setContinueExistingPeriodicTasksAfterShutdownPolicy方法

void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value)

 默认为false。在线程池执行shutdown方法后是否继续执行scheduleAtFixedRate方法和scheduleWithFixedDelay方法提交的任务

2.6 setExecuteExistingDelayedTasksAfterShutdownPolicy方法

void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value)

 默认为true,在线程池执行shutdown方法后,需要等待当前正在等待的任务的和正在运行的任务被执行完,然后进程被销毁。为false时,表示放弃等待的任务,正在运行的任务一旦完成,则进程被销毁。

2.7 scheduleAtFixedRatescheduleWithFixedDelay的区别

直白地讲,scheduleAtFixedRate()为固定频率,scheduleWithFixedDelay()为固定延迟。固定频率是相对于任务执行的开始时间,而固定延迟是相对于任务执行的结束时间,这就是他们最根本的区别!

三、练习

3.1 schedule练习1

本次练习中除了schedule的练习外,还包含了如何取消任务。

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorServiceExample {
    public static void main(String[] args) {
        // 创建定时任务线程池,核心线程数为2
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
        // 2秒后执行Runnable任务
        scheduledThreadPoolExecutor.schedule(() -> {
            System.out.println("This is runable1 task");
        }, 2, TimeUnit.SECONDS);
        // 再提交一个2秒后才执行的Runnable任务
        // 既然Runnable无法返回结果,为什么还要有Future呢,因为我们可以通过Future进行取消任务等操作
        ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.schedule(() -> {
            System.out.println("This is runable2 task");
        }, 2, TimeUnit.SECONDS);
        // 取消任务
        runnableFuture.cancel(true);
        // 休眠3秒,确保上面的任务都被执行完
        mySleep(3);
        System.out.println("========================");
    }

    private static void mySleep(int seconds){
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

This is runable1 task

========================

3.2 schedule练习2

@Test public void test_schedule4Runnable() throws Exception {
    // 创建定时任务线程池
    ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
    // 延迟执行任务
    ScheduledFuture future = service.schedule(() -> {
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("task finish time: " + format(System.currentTimeMillis()));
    }, 1000, TimeUnit.MILLISECONDS);
    System.out.println("schedule finish time: " + format(System.currentTimeMillis()));
    // 通过get方法阻塞当前线程,直到任务执行完毕
    System.out.println("Runnable future's result is: " + future.get() +
                       ", and time is: " + format(System.currentTimeMillis()));
}

上述代码达到的效果应该是这样的:延迟执行时间为1秒,任务执行3秒,任务只执行一次,同时通过Future.get()阻塞直至任务执行完毕。

我们运行看到的效果的确和我们猜想的一样,如下图所示。

在schedule Runnable的基础上,我们将Runnable改为Callable来看一下。

@Test public void test_schedule4Callable() throws Exception {
    // 创建定时任务线程池
    ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
    // 提交一个带返回值的Callable任务,延迟1秒执行
    ScheduledFuture<String> future = service.schedule(() -> {
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("task finish time: " + format(System.currentTimeMillis()));
        return "success";
    }, 1000, TimeUnit.MILLISECONDS);
    System.out.println("schedule finish time: " + format(System.currentTimeMillis()));
    // 通过future.get()方法获取任务执行结果,如果任务还没执行完,则会阻塞等待
    System.out.println("Callable future's result is: " + future.get() +
            ", and time is: " + format(System.currentTimeMillis()));
}

运行看到的结果和Runnable基本相同,唯一的区别在于future.get()能拿到Callable返回的真实结果。

3.3 scheduleAtFixedRate练习1

周期性执行某个任务,执行到一定时间后取消任务

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceExample2 {
    public static void main(String[] args) {
        // 创建定时任务线程池
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
        // 提交延迟1秒执行,周期为2秒的runnable任务,虽然Runnable没有返回结果,但是可以通过runnableFuture取消任务
        ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
            System.out.println("This is runable task running "+Thread.currentThread().getName());
        }, 1,2, TimeUnit.SECONDS);
        // 休眠8秒
        mySleep(8);
        // 取消该循坏任务
        runnableFuture.cancel(true);
    }
    
    private static void mySleep(int seconds){
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

  • 可以看出每个周期执行的任务并不是同一个线程,周期时间到的时候只是将任务扔到线程池的任务队列中由空闲线程获取它的执行权。

This is runable task running pool-1-thread-1

This is runable task running pool-1-thread-1

This is runable task running pool-1-thread-1

This is runable task running pool-1-thread-2

3.4 scheduleAtFixedRate练习2

超时的周期性任务

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class ScheduledExecutorServiceExample3 {
    public static void main(String[] args) {
        // 创建定时任务线程池
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
        // 创建一个原子计数器
        AtomicLong atomicLong = new AtomicLong(0L);
        // 提交初始延迟1秒执行,固定周期为2秒的Runnable任务
        ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
            // 记录当前时间
            Long current = System.currentTimeMillis();
            // 判断是否为第一次运行
            if (atomicLong.get()==0) {
                atomicLong.set(current);
                System.out.printf("first running [%d]\n",atomicLong.get());
            } else {
                // 记录与上次的间隔时间
                System.out.printf("running time:[%d]\n",current-atomicLong.get());
            }
            // 将当前时间保存
            atomicLong.set(current);
            // 模拟超过固定周期时间,故意让任务晚结束,导致超过本轮周期时长了
            mySleep(5);
        }, 1,2, TimeUnit.SECONDS);
    }
    private static void mySleep(int seconds){
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

  • 可以看出,超出周期时间时,任务完成后立即就进入了下一周期

first running [1597659726690]

running time:[5042]

running time:[5001]

running time:[5000]

running time:[5001]

 

 

3.5 scheduleWithFixedDelay练习

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

public class ScheduledExecutorServiceExample4 {
    public static void main(String[] args) {
        // 创建定时任务线程池
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
        // 创建原子计数器
        AtomicLong atomicLong = new AtomicLong(0L);
        // 提交初始延迟1秒执行,延迟为2秒的runnable任务
        ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
            // 记录当前时间
            Long current = System.currentTimeMillis();
            // 判断是否为第一次运行
            if (atomicLong.get() == 0){
                atomicLong.set(current);
                System.out.printf("first running [%d]\n",atomicLong.get());
            }else{
                //记录与上次的间隔时间
                System.out.printf("running time:[%d]\n",current-atomicLong.get());
            }
            // 将当前时间保存
            atomicLong.set(current);
            // 模拟超过固定周期时间   使其运行时间超过周期时间
            mySleep(5);
        }, 1,2, TimeUnit.SECONDS);
    }
    private static void mySleep(int seconds){
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

  • 可以看出来,无论你的任务执行多久,在任务执行完毕之后都会延迟一个调度周期时间才进入下一周期。

first running [1597659862349]

running time:[7047]

running time:[7002]

running time:[7023]

running time:[7002]

running time:[7003]

3.6 setContinueExistingPeriodicTasksAfterShutdownPolicy练习

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceExample5 {
    public static void main(String[] args) {
        // 创建定时任务线程池
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2);
        // 提交固定周期任务
        ScheduledFuture<?> runnableFuture = scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
            System.out.println("This is runable task running "+Thread.currentThread().getName());
        }, 1,2, TimeUnit.SECONDS);
        // 默认情况关闭线程池后是不允许继续执行固定周期任务的,所有输出false
        System.out.println(scheduledThreadPoolExecutor.getContinueExistingPeriodicTasksAfterShutdownPolicy());
        // 设置为true
        scheduledThreadPoolExecutor.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
        // 休眠1200毫秒,确保任务被执行
        mySleep(1200);
        // 关闭线程池
        scheduledThreadPoolExecutor.shutdown();
        // 休眠2000毫秒后查看线程池状态
        mySleep(2000);
        // 线程池的状态
        System.out.println("isShutdown:"+scheduledThreadPoolExecutor.isShutdown());
        System.out.println("isTerminating:"+scheduledThreadPoolExecutor.isTerminating());
        System.out.println("isTerminated:"+scheduledThreadPoolExecutor.isTerminated());
    }
    
    private static void mySleep(int milliSeconds){
        try {
            TimeUnit.MILLISECONDS.sleep(milliSeconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

  • 可以看出来,设置为true之后,即使线程池关闭了,定时任务仍然在执行

false

This is runable task running pool-1-thread-1

This is runable task running pool-1-thread-1

isShutdown:true

isTerminating:true

isTerminated:false

This is runable task running pool-1-thread-1

This is runable task running pool-1-thread-1

...


相关文章:【线程池】Java的线程池
                  【线程池】Java线程池的核心参数
                  【线程池】Executors框架创建线程池

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

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

相关文章

什么是组件,以及前端各种框架组件的使用方法

&#x1f642;博主&#xff1a;小猫娃来啦 &#x1f642;文章核心&#xff1a;介绍什么是组件&#xff0c;以及前端各种框架组件的使用方法 目录 什么是组件&#xff0c;请细看关键点 前端常用框架vue2框架的常用组件库vue3框架的常用组件库react框架的常用组件库微信小程序常用…

智能三路CAN总线集线器解决CAN总线集线问题

随着科技的发展&#xff0c;CAN总线通讯再各行各业中应用&#xff0c;再不同的应用中&#xff0c;需要把两路CAN集合到一起&#xff0c;或者把一路CAN分为两路&#xff0c;这时候就需要CAN总线集线器来解决。 CANbridge-300每一路CAN通道都具有数据存储转发至另两路CAN通道的功…

Java——《面试题——分布式篇》

前文 java——《面试题——基础篇》 Java——《面试题——JVM篇》 Java——《面试题——多线程&并发篇》 Java——《面试题——Spring篇》 Java——《面试题——SpringBoot篇》 Java——《面试题——MySQL篇》​​​​​​ Java——《面试题——SpringCloud》 Java…

SQL查询语句语法结构和运行顺序

语法结构&#xff1a;select–from–where–group by–having–order by–limit 运行顺序&#xff1a;from–where–group by–having–order by–limit–select

【Vue3】学习笔记-生命周期

Vue2.x与vue3生命周期 Vue3.0中可以继续使用Vue2.x中的生命周期钩子&#xff0c;但有有两个被更名&#xff1a; beforeDestroy改名为 beforeUnmountdestroyed改名为 unmounted Vue3.0也提供了 Composition API 形式的生命周期钩子&#xff0c;与Vue2.x中钩子对应关系如下&#…

只用一次集合遍历实现树形结构,非递归方式

一般情况下&#xff0c;我们想要实现这种无限层级的树形结构&#xff0c;都是采用递归的方式&#xff0c;但是递归比较占用内存&#xff0c;也容易导致栈溢出&#xff0c;于是只能尝试其它的方法。 下面采用的方式&#xff0c;只需要一次集合的遍历就可以实现树形的结构。 先手…

Spring Boot 中的 @RabbitListener 注解是什么,原理,如何使用

Spring Boot 中的 RabbitListener 注解是什么&#xff0c;原理&#xff0c;如何使用 在 RabbitMQ 中&#xff0c;消息的接收需要通过监听队列来实现。在 Spring Boot 应用程序中&#xff0c;可以使用 RabbitListener 注解来监听队列&#xff0c;并在接收到消息时执行指定的方法…

Java实现OpenAI模型训练

本文章介绍如何用java实现OpenAI模型训练&#xff0c;仅供参考 提前准备工作 OpenAI KEY&#xff0c;获取方式可自行百度需要自备VPN 或 使用国外服务器转发需要训练的数据集&#xff0c;文章格式要求为JSONL&#xff0c;格式内容详见下图&#xff08;尽量不要低于500个问答&…

消息模块的数据模型设计

目录 一、创建POJO映射类 1. message集合 2. message_ref集合 二、MongoDB的联合查询 MessageRef&#xff1a;保存消息接收人相关信息&#xff0c;接收人ID、消息已读未读、消息是否最新接收。 在 MongoDB 里面我们不需要预先去创建集合&#xff0c;只要我们往 MongoDB 里…

Linux常用命令——finger命令

在线Linux命令查询工具 finger 用于查找并显示用户信息 补充说明 finger命令用于查找并显示用户信息。包括本地与远端主机的用户皆可&#xff0c;帐号名称没有大小写的差别。单独执行finger指令&#xff0c;它会显示本地主机现在所有的用户的登陆信息&#xff0c;包括帐号名…

Layout-静态模板结构搭建、字体图标引入、一级导航渲染、吸顶导航交互实现、Pinia优化重复请求【小兔鲜Vue3】

Layout-静态模板结构搭建 Layout模块静态模板搭建 LayoutNav.vue <script setup></script><template><nav class"app-topnav"><div class"container"><ul><template v-if"true"><li><a h…

鲸鱼算法WOA优化VMD参数,最小包络熵、样本熵、信息熵、排列熵(适应度函数可自行选择,一键修改)包含MATLAB源代码...

鲸鱼优化算法(Whale optimization algorithm, WOA)是Mirjalili根据座头鲸的捕食行为而提出来的&#xff0c;算法对座头鲸的狩猎行为进行模仿&#xff0c;通过对猎物的寻找&#xff0c;然后攻击进行觅食&#xff0c;以此来达到优化的目的&#xff0c;已有很多学者将算法用于实际…

【C/C++实现进程间通信 二】消息队列

文章目录 前情回顾思路源码Publisher.cppSubscriber.cpp 效果 前情回顾 上一期已经讲解过了进程的相关概念以及进程间通信的实现原理&#xff0c;下面仅展示消息传递机制实现1进程间通信的相关代码。 思路 /*本项目主要用于以消息传递机制的方式进行进程间通信的测试。1.主要…

很荣幸与屈晗煜在 GitLink 编程夏令营(GLCC)活动中协作

作为一名软件工程师&#xff0c;天然有着追求代码质量的执念。相信很多人对代码的优雅、质量有着自己的认识&#xff0c;业界也有不少的共识&#xff0c;其中有一条我认为是非常重要的——代码可测试。 作为一名研发&#xff0c;只关注功能可测&#xff08;容易测&#xff09;是…

BUUCTF old-fashion 1

题目描述&#xff1a; 密文&#xff1a; Os drnuzearyuwn, y jtkjzoztzoes douwlr oj y ilzwex eq lsdexosa kn pwodw tsozj eq ufyoszlbz yrl rlufydlx pozw douwlrzlbz, ydderxosa ze y rlatfyr jnjzli; mjy gfbmw vla xy wbfnsy symmyew (mjy vrwm qrvvrf), hlbew rd symmy…

多元回归预测 | Matlab基于鲸鱼算法(WOA)优化混合核极限学习机HKELM回归预测, WOA-HKELM数据回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于鲸鱼算法(WOA)优化混合核极限学习机HKELM回归预测, WOA-HKELM数据回归预测,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 …

【软考程序员学习笔记】——数据结构与算法基础

目录 &#x1f34a;一、数据结构概念和分类 &#x1f34a;二、数组特点&存储方式 &#x1f34a;三、矩阵 特殊矩阵 非特殊矩阵 &#x1f34a;四、栈和队列 &#x1f34a; 五、二叉树的性质 &#x1f34a;六、二叉树的遍历 (1)前序遍历(先根遍历&#xff0c;先序遍历…

《MySQL》什么是数据库

文章目录 数据库的理解MySQL的架构SQL语句分类存储引擎 数据库的理解 我们所下的数据库软件(如MySQL)其实是中间件。如何理解呢&#xff0c;如图&#xff1a; 我们用户通过MySQL对数据进行管理。 mysql #客户端程序 mysqld #服务端程序&#xff0c;再修改配置后需要重新启动…

【MySQL】在Linux终端上使用MySQL的简单介绍

本文仅供学习参考&#xff01; MySQL是一个开源的跨平台关系数据库管理系统&#xff08;RDBMS&#xff09;。MySQL使用标准查询语言&#xff08;SQL&#xff09;与MySQL软件进行通信。 数据库管理员和开发人员通过运行语句与 RDBMS 交互&#xff0c;这些语句在数据库管理中称为…

前端Vue自定义登录界面切换tabs 账号登录验证码登录模版 支持手机号校验邮箱校验验证码发送

前端Vue自定义登录界面切换tabs 账号登录验证码登录模版 支持手机号校验邮箱校验、验证码发送&#xff0c; 下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13298 效果图如下&#xff1a; # cc-loginTabs #### 使用方法 使用方法 …