Quartz学习总结之核心接口Scheduler、Job

2023-10-29

参考文章:https://www.cnblogs.com/mengrennwpu/p/7141986.html

核心接口如下:

接口 含义
Scheduler scheduler的主要API接口
Job 任务实现接口,期望调度器能够执行
JobDetail 用于定义Job实例
Trigger 调度器基于特定时间来执行指定任务的组件
JobBuilder   用于定义、创建JobDetail实例
TriggerBuilder 用于定义、创建Trigger实例

1、Scheduler

       调度器Scheduler就相当于一个容器,装载着任务和触发器。该类是一个接口,代表一个 Quartz 的独立运行容器, Trigger 和 JobDetail 可以注册到 Scheduler 中, 两者在 Scheduler 中拥有各自的组及名称, 组及名称是 Scheduler 查找定位容器中某一对象的依据, Trigger 的组及名称必须唯一, JobDetail 的组和名称也必须唯一(但可以和 Trigger 的组和名称相同,因为它们是不同类型的)。Scheduler 定义了多个接口方法, 允许外部通过组及名称访问和控制容器中 Trigger 和 JobDetail。

       Scheduler 可以将 Trigger 绑定到某一 JobDetail 中, 这样当 Trigger 触发时, 对应的 Job 就被执行。一个 Job 可以对应多个 Trigger, 但一个 Trigger 只能对应一个 Job。可以通过 SchedulerFactory 创建一个 Scheduler 实例。Scheduler 拥有一个 SchedulerContext,保存着 Scheduler 上下文信息,Job 和 Trigger 都可以访问 SchedulerContext 内的信息。SchedulerContext 内部通过一个 Map,以键值对的方式维护这些上下文数据,SchedulerContext 为保存和获取数据提供了个 put() 和 getXxx() 的方法。可以通过 Scheduler.getContext() 获取对应的 SchedulerContext 实例。

       一个调度器的生命周期为通过SchedulerFactory创建,直到执行其shutdown()方法。当Scheduler创建之后,可以进行增加、删除及显示任务Job与触发器Trigger,并且执行其他的调度相关的操作,如暂停一个触发器Trigger。需要注意的是,直到调用start()方法时,Scheduler才正式开始执行job和trigger。

       StdSchedulerFactory用于创建Scheduler,其依赖于一系列的属性来决定如何产生Scheduler。可以通过四种途径向StdSchedulerFactory提供属性配置信息。

1.1 通过java.util.Properties初始化StdSchedulerFactory

       在Quzrtz学习总结一之快速体验的工程基础上,创建一个包com.zdw.zixue.scheduler,该包用来测试通过四种方式来初始化StdSchedulerFactory对象

package com.zdw.zixue.scheduler;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Properties;

public class SchedulerDemo1 {
    public static void main(String[] args) {
        //创建StdSchedulerFactory实例
        StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();

        //创建配置工厂的属性对象
        Properties properties = new Properties();
        properties.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,"org.quartz.simpl.SimpleThreadPool");//定义线程池
        properties.put("org.quartz.threadPool.threadCount","10");//默认Scheduler的线程数

        try {
            //使用属性对象初始化工厂
            stdSchedulerFactory.initialize(properties);
            //创建调度容器Scheduler
            Scheduler scheduler = stdSchedulerFactory.getScheduler();
            //启动调度容器
            scheduler.start();
            System.out.println("调度容器的元数据:"+scheduler.getMetaData());
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

执行,控制台打印:

调度容器的元数据:Quartz Scheduler (v2.2.3) 'QuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.impl.StdScheduler' - running locally.
  Running since: Wed Sep 25 16:07:27 CST 2019
  Not currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

可以看到对应的配置属性已经生效。

       通过Properties设置工厂属性的缺点在用硬编码,假如需要修改例子中线程数量,将不得不修改代码,然后重新编译。后面几种方法可以解决硬编码的问题。

1.2 通过外部属性文件初始化StdSchedulerFactory

假设在D盘的根目录下有一个文件:properties.properties,(文件名随意),里面的内容如下:

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=5
package com.zdw.zixue.scheduler;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

public class SchedulerDemo2 {
    public static void main(String[] args) {
        //创建StdSchedulerFactory实例
        StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();

        try {
            //使用外部属性文件初始化工厂
            stdSchedulerFactory.initialize("d:/properties.properties");
            //创建调度容器Scheduler
            Scheduler scheduler = stdSchedulerFactory.getScheduler();
            //启动调度容器
            scheduler.start();
            System.out.println("2--调度容器的元数据:"+scheduler.getMetaData());
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

执行结果:

2--调度容器的元数据:Quartz Scheduler (v2.2.3) 'QuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.impl.StdScheduler' - running locally.
  Running since: Wed Sep 25 16:11:48 CST 2019
  Not currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 5 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

1.3 通过含有属性文件内容的java.io.InputStream初始化StdSchedulerFactory

假设在D盘的根目录下有一个文件:properties.properties,(文件名随意),里面的内容如下:

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=5
package com.zdw.zixue.scheduler;

import org.quartz.Scheduler;
import org.quartz.impl.StdSchedulerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class SchedulerDemo3 {
    public static void main(String[] args) {
        //创建StdSchedulerFactory实例
        StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
        try {
            //得到D盘下的配置文件properties.properties的字节流
            InputStream inputStream = new FileInputStream(new File("d:/properties.properties"));
            //使用含有属性文件内容的java.io.InputStream初始化工厂
            stdSchedulerFactory.initialize(inputStream);
            //创建调度容器Scheduler
            Scheduler scheduler = stdSchedulerFactory.getScheduler();
            //启动调度容器
            scheduler.start();
            System.out.println("3---调度容器的元数据:"+scheduler.getMetaData());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

3--调度容器的元数据:Quartz Scheduler (v2.2.3) 'QuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.impl.StdScheduler' - running locally.
  Running since: Wed Sep 25 16:25:49 CST 2019
  Not currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 5 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

1.4 通过quartz.properties配置文件初始化StdSchedulerFactory

这是推荐使用的方式,也是开发中经常使用的方式。

       如果调用无参的initialize方法,StdSchedulerFactory会试图从quartz.properties的文件中加载。quartz.properties相关配置后续文章会介绍,注意quartz.properties的加载顺序为:

a. 检查System.getProperty("org.quartz.properties")中是否设置其他属性文件名

b. 如果a未设置,则将会从当前工作目录中加载quartz.properties配置文件

c. 如果b未找到,则试图从系统的classpath中加载该配置文件。

       Scheduler在生命周期中也可执行其他操作,如查询、设置standby模式、继续执行、停止执行。standby模式会导致Scheduler暂时停止查找Job去执行。standby模式的设置直接使用scheudler.standby()即可。

       Scheduler的停止方法为shutdown()方法,也可以使用有参shutdown(false),其中参数表示是否让当前正在进行的job正常执行完成才停止Scheduler。

 

本次,我们在工程的resources目录下面创建quartz.properties文件,内容如下:

org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=8
package com.zdw.zixue.scheduler;

import org.quartz.Scheduler;
import org.quartz.impl.StdSchedulerFactory;

public class SchedulerDemo4 {
    public static void main(String[] args) {
        //创建StdSchedulerFactory实例
        StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
        try {
            //使用classpath下的quartz.properties配置文件初始化工厂
            stdSchedulerFactory.initialize();
            //创建调度容器Scheduler
            Scheduler scheduler = stdSchedulerFactory.getScheduler();
            //启动调度容器
            scheduler.start();
            System.out.println("4--调度容器的元数据:"+scheduler.getMetaData());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

4--调度容器的元数据:Quartz Scheduler (v2.2.3) 'QuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.impl.StdScheduler' - running locally.
  Running since: Wed Sep 25 16:35:30 CST 2019
  Not currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 8 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

 

2、Job

      定义需要执行的任务。该类是一个接口,只定义一个方法 execute(JobExecutionContext context),在实现类的 execute 方法中编写所需要定时执行的 Job(任务), JobExecutionContext 类提供了调度应用的一些信息,JobExecutionContext对象让Job能访问Quartz运行时环境的所有信息和Job本身的明细数据。运行时环境信息包括注册到Scheduler上与该Job相关联的JobDetail和Trigger。

修改Quzrtz学习总结一之快速体验的工程中的FirstJob.java文件,如下:

package com.zdw.zixue.job;

import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Job实现类,任意java类实现Job接口就行了
 */
public class FirstJob implements Job {

    private static Logger logger = LoggerFactory.getLogger(FirstJob.class);//日志对象

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("----------Hello World--------------");

        // 每一个Job都有其自己所属的JobDetail
        JobDetail jobDetail = context.getJobDetail();
        //JobDetail的名称和组名
        System.out.println("JobDetail的名称和组名:"+jobDetail.getKey());

        // 获取Scheduler
        Scheduler scheduler = context.getScheduler();
        try {
            System.out.println("调度器Scheduler name: "+scheduler.getSchedulerName());
        } catch (SchedulerException e) {
            e.printStackTrace();
        }

        // 具体job的类全名
        System.out.println("job的类全名:"+jobDetail.getJobClass());

        // 本次任务执行时间
        System.out.println("本次任务执行时间:"+context.getFireTime());

        // 下次任务执行时间
        System.out.println("下次任务执行时间:"+context.getNextFireTime());

        // 获取jobDetail的参数信息
        System.out.println("参数信息:"+jobDetail.getJobDataMap().get("message"));
    }
}

再次运行FirstJobTest,结果:

----------创建调度任务成功----------
----------Hello World--------------
JobDetail的名称和组名:first_group.first_job
调度器Scheduler name: QuartzScheduler
job的类全名:class com.zdw.zixue.job.FirstJob
本次任务执行时间:Wed Sep 25 16:49:57 CST 2019
下次任务执行时间:Wed Sep 25 16:50:07 CST 2019
参数信息:null
----------Hello World--------------
JobDetail的名称和组名:first_group.first_job
调度器Scheduler name: QuartzScheduler
job的类全名:class com.zdw.zixue.job.FirstJob
本次任务执行时间:Wed Sep 25 16:50:07 CST 2019
下次任务执行时间:Wed Sep 25 16:50:17 CST 2019
参数信息:null
----------Hello World--------------
JobDetail的名称和组名:first_group.first_job
调度器Scheduler name: QuartzScheduler
job的类全名:class com.zdw.zixue.job.FirstJob
本次任务执行时间:Wed Sep 25 16:50:17 CST 2019
下次任务执行时间:Wed Sep 25 16:50:27 CST 2019
参数信息:null
----------Hello World--------------
JobDetail的名称和组名:first_group.first_job
调度器Scheduler name: QuartzScheduler
job的类全名:class com.zdw.zixue.job.FirstJob
本次任务执行时间:Wed Sep 25 16:50:27 CST 2019
下次任务执行时间:Wed Sep 25 16:50:37 CST 2019
参数信息:null
---------------关闭了调度器----------------

3、JobDetail

       描述 Job 的实现类及其它相关的静态信息,如:Job 名字、描述、关联监听器等信息。Quartz 每次调度 Job 时, 都重新创建一个 Job 实例, 所以它不直接接受一个 Job 的实例,相反它接收一个 Job 实现类,以便运行时通过 newInstance() 的反射机制实例化 Job。

3.1 Job简述

       JobDetail是作为Job实例进行定义的,注意部署在Scheduler上的每一个Job只创建一个JobDetail实例。且需要注意的是注册到Scheduler上的不是Job对象,而是JobDetail实例。

      Job 的实例要到该执行它们的时候才会实例化出来。每次 Job 被执行,一个新的 Job 实例会被创建。其中暗含的意思就是你的 Job 不必担心线程安全性,因为同一时刻仅有一个线程去执行给定 Job 类的实例,甚至是并发执行同一 Job 也是如此。

       可以使用JobDataMap来定义Job的状态,JobDataMap中可以存入key-value对,这些数据可以在Job实现类中进行传递和访问。这是向你的Job传送配置信息的便捷方法。

      Job 能通过 JobExecutionContext 对象访问 JobDataMap

修改Quzrtz学习总结一之快速体验的工程中的FirstJobTest.java文件,把睡眠时间修改为20s,并且把类QuartzServer中的createJobDetail方法修改为:

//创建JobDetail
    public static JobDetail createJobDetail(){
        JobDetail jobDetail = JobBuilder.newJob(FirstJob.class)//设置执行任务的类
                .withIdentity("first_job", "first_group")//job名称与job组名称组成Scheduler中任务的唯一标识
                .usingJobData("message","我是第一个job的参数")//设置参数信息
                .build();//构建
        return jobDetail;
    }

运行结果:

----------创建调度任务成功----------
----------Hello World--------------
JobDetail的名称和组名:first_group.first_job
调度器Scheduler name: QuartzScheduler
job的类全名:class com.zdw.zixue.job.FirstJob
本次任务执行时间:Wed Sep 25 17:02:26 CST 2019
下次任务执行时间:Wed Sep 25 17:02:36 CST 2019
参数信息:我是第一个job的参数
----------Hello World--------------
JobDetail的名称和组名:first_group.first_job
调度器Scheduler name: QuartzScheduler
job的类全名:class com.zdw.zixue.job.FirstJob
本次任务执行时间:Wed Sep 25 17:02:36 CST 2019
下次任务执行时间:Wed Sep 25 17:02:46 CST 2019
参数信息:我是第一个job的参数
----------Hello World--------------
JobDetail的名称和组名:first_group.first_job
调度器Scheduler name: QuartzScheduler
job的类全名:class com.zdw.zixue.job.FirstJob
本次任务执行时间:Wed Sep 25 17:02:46 CST 2019
下次任务执行时间:Wed Sep 25 17:02:56 CST 2019
参数信息:我是第一个job的参数
---------------关闭了调度器----------------

可以看到,在FirstJob中,可以获取到我们设置的参数message的值。

       如果你使用的是持久化的存储机制(本教程的JobStore部分会讲到),在决定JobDataMap中存放什么数据的时候需要小心,因为JobDataMap中存储的对象都会被序列化,因此很可能会导致类的版本不一致的问题;Java的标准类型都很安全,如果你已经有了一个类的序列化后的实例,某个时候,别人修改了该类的定义,此时你需要确保对类的修改没有破坏兼容性;

3.2 有状态Job和无状态Job

       有状态的Job可以理解为多次Job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中,而默认的无状态job每次调用时都会创建一个新的JobDataMap。

3.2.1 有状态的Job示例

靠注解@PersistJobDataAfterExecution实现

修改类QuartzServer中的createJobDetail方法,设置参数的值,如下:

//创建JobDetail
    public static JobDetail createJobDetail(){
        JobDetail jobDetail = JobBuilder.newJob(FirstJob.class)//设置执行任务的类
                .withIdentity("first_job", "first_group")//job名称与job组名称组成Scheduler中任务的唯一标识
                //.usingJobData("message","我是第一个job的参数")//设置参数信息
                .usingJobData("count",0)//设置参数
                .build();//构建
        return jobDetail;
    }

FirstJob修改为如下:

package com.zdw.zixue.job;

import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@PersistJobDataAfterExecution//添加了这个注解
public class FirstJob implements Job {
    private static Logger logger = LoggerFactory.getLogger(FirstJob.class);//日志对象
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();

        int count = jobDataMap.getInt("count");
        System.out.println("count: "+count);
        ++count;
        jobDataMap.put("count", count);
    }
}

运行结果:

----------创建调度任务成功----------
count: 0
count: 1
count: 2
---------------关闭了调度器----------------

3.2.2 无状态的Job示例

不加注解@PersistJobDataAfterExecution

package com.zdw.zixue.job;

import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FirstJob implements Job {
    private static Logger logger = LoggerFactory.getLogger(FirstJob.class);//日志对象
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();

        int count = jobDataMap.getInt("count");
        System.out.println("count: "+count);
        ++count;
        jobDataMap.put("count", count);
    }
}

运行结果:

----------创建调度任务成功----------
count: 0
count: 0
count: 0
---------------关闭了调度器----------------

可见 @PersistJobDataAfterExecution的作用在于持久化保存在JobDataMap中的传递参数,使得多次执行Job,可以获取传递参数的状态信息。

Job 有一个 StatefulJob 子接口(Quartz 2 后用 @PersistJobDataAfterExecution 注解代替),代表有状态的任务,该接口是一个没有方法的标签接口,其目的是让 Quartz 知道任务的类型,以便采用不同的执行方案。

  • 无状态任务在执行时拥有自己的 JobDataMap 拷贝,对 JobDataMap 的更改不会影响下次的执行。

  • 有状态任务共享同一个 JobDataMap 实例,每次任务执行对 JobDataMap 所做的更改会保存下来,后面的执行可以看到这个更改,也即每次执行任务后都会对后面的执行发生影响。

正因为这个原因,无状态的 Job 并发执行,而有状态的 StatefulJob 不能并发执行。这意味着如果前次的 StatefulJob 还没有执行完毕,下一次的任务将阻塞等待,直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素,程序往往拥有更高的复杂度,因此除非必要,应该尽量使用无状态的 Job。

 

3.3 @DisallowConcurrentExecution 

       quartz中另一个常用的注解为@DisallowConcurrentExecution,该注解可以同一个时刻,同一个任务只能执行一次,不能并行执行两个或多个同一任务。但需要注意的是,多个不同的任务是可以同时执行的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Quartz学习总结之核心接口Scheduler、Job 的相关文章

随机推荐

  • Unable to cast object of type in System.DirectoryServices.AccountManagement.GroupPrincipal

    在使用UserPrincipal Current ToString 获取域登录用户信息时 本地调试没有问题 上传到服务器报错 Unable to cast object of type System DirectoryServices Ac
  • CSwin-PNet: CNN-Swin-Vit 组合金字塔网络用于超声图像中乳腺病变分割

    ATTransUNet 期刊分析 摘要 贡献 方法 整体框架 1 Residual Swin Transformer block 2 Interactive channel attention module 3 Supplementary
  • java数据类型

    整数类型 byte short int long 浮点数类型 float double 字符类型 String 布尔类型 boolean 1 整数类型 byte 128 127 short 32768 32767 int 214748364
  • CTFHub-时间盲注-wp #(自制脚本做法)

    时间盲注脚本 coding utf 8 Time 2021 5 16 19 29 Author z1moq File ctfhub时间盲注 py Software PyCharm import requests import string
  • MATLAB实现控制系统模型(传递函数)的建立与转化,传递函数模型与零极点增益模型的转化,连续系统与离散系统的转化,对比不同采样周期对系统性能的影响

    最近使用MATLAB做了很多控制工程方面的仿真 测试不同系统的响应和特性 不得不说使用MATLAB做控制仿真还是十分简洁方便的 尤其是其中的simulink模块可以提供更加直观的模型 方便分析与测试 今天就分享在matlab中构造传递函数模
  • 【微信小程序】微信支付接入全流程

    一 前置条件 接入支付首先得需要有企业资质 并开通企业对公户 注册微信支付并进行对公户打款认证 二 开始接入 1 下载微信支付的AP证书 2 服务端接入微信支付 2 1 引入相关maven配置
  • Java基础之集合

    Java基础 集合 1 Collection接口 Collection 是 List 和 Set 的父接口 常用方法如下 package com java day16 import java util ArrayList import ja
  • 简单实现继承一个抽象类的同时实现接口

    定义一个抽象类animal author ljf 定义一个抽象类animal 关键字abstract public abstract class Animal 将动物共有属性进行封装 名字 年龄 颜色 性别 说话 private Strin
  • np.dot(a, b)用法

    In short np dot a b 就是一个乘法函数 数和数相乘 若a和b都是数 np dot 1 2 2 一维数组的内积 np dot 1 2 3 4 5 6 1 2 3 4 5 6 1x4 2x5 3x6 32 矩阵的乘积 x np
  • Unity学习笔记——TextMeshPro使用详解

    https blog csdn net elineSea article details 88799896 TextMesh Pro是Unity默认文本组件的替代品 TextMesh Pro和默认组件一样拥有高性能 它使用了完全不同的Sig
  • 为什么大部分人认为测试用例不重要?如何正确编写软件测试用例?

    如何编写测试用例似乎不是开发的重要部分 但是为了让一个软件测试人员最好地完成他们的工如如何编写测试用例似乎不是开发的重要部分 但是为了让一个软件测试人员最好地完成他们的作 他们需要一套清晰的步骤和一个被测试的东西的清晰定义 编写优秀的测试用
  • 代码审查常见代码质量问题

    配套的Bug解释模式 为了有针对性的使用这个工具 减少bug的误报 提高使用效率 我们选择了10个左右的bug模式 下面就是对这10个模式的解释 这些bug可能会引起程序的性能或逻辑问题 需要说明的是 findbugs能检测的bug pat
  • 有哪些好用的设计图工具?

    设计图纸制作软件是高级学习数字设计的最佳选择 无论你是想通过设计图纸制作软件创建一个明亮的设计 还是与其他设计师分享和交流 本文将介绍十个易于使用的设计图纸制作软件 其中大多数是初学者和高级艺术家 具有完整的绘图 照片编辑和小图形设计项目功
  • jmeter 安装_JMeter安装教程

    一 安装JMeter之前我们需要下载Java的jdk Java软件开发工具包 这是因为JMeter软件是由Java代码100 开发的 Java代码要运行必须依托于JVM Java虚拟机 因此JMeter如果要运行也必须要在有JVM环境的系统
  • STM32学习记录 中断

    STM32 中断非常强大 每个外设都可以产生中断 中断类型有 系统异常 外部中断 NVIC 嵌套向量中断控制器 属于内核外设 管理着包括内核和片上所有外设的中断相关的功能 两个重要的库文件 core cm3 h和misc h 中断编程的顺序
  • C语言文件操作详解

    目录 前言 一 文本数据和二进制数据 文本数据 二进制数据 文本文件和二进制文件 二 文件的打开和关闭 文件指针 打开文件 小细节 关闭文件 举个例子 注意事项 三 文本文件的读写 向文件中写入数据 举个例子 运行效果 从文件中读取数据 举
  • 什么是黑盒测试,和白盒测试的区别有哪些?

    软件测试是软件工程中的一个非常重要的环节 是开发项目整体的一部分 是伴随软件工程的诞生而诞生的 但软件测试不是万能的 不可能发现全部缺陷 其中 黑盒测试和白盒测试是两种不同类型的软件测试策略 它们具有同样强大的功能 白盒测试和黑盒测试往往不
  • 浅谈Android版本更新

    关于本文DownloadManager版本更新的源码链接详见我的开源项目AppUpdate 前言 版本升级对于app来讲已经是非常常见的功能了 每次项目的版本迭代 新功能的开发都需要下载更新新版本 通过安装新版本来实现我们的迭代 当然除了这
  • 图解Redis中的9种数据结构

    如图所示 Redis中提供了9种不同的数据操作类型 他们分别代表了不同的数据存储结构 图2 17 数据类型 String类型 String类型是Redis用的较多的一个基本类型 也是最简单的一种类型 它和我们在Java中使用的字符类型什么太
  • Quartz学习总结之核心接口Scheduler、Job

    参考文章 https www cnblogs com mengrennwpu p 7141986 html 核心接口如下 接口 含义 Scheduler scheduler的主要API接口 Job 任务实现接口 期望调度器能够执行 JobD