引言

在实际项目开发中,定时任务调度是经常会出现的一类需求。

定时任务的场景可以说非常广泛,例如:

购买某些视频网站的会员后,每天给会员送成长值,每月给会员送电影券

在保证最终一致性的场景中,利用定时任务调度进行一些数据核对的工作

通过邮件定时发送报表和工作提醒

需要定时清理数据的任务

本文将介绍单机定时任务的基本实现方式,可以覆盖到定时任务调度最基本的使用场景:包括:

Timer与TimerTask

ScheduledExecutorService

Spring Task

Quartz

正文

方式一:JDK原生定时工具:Timer

简介

JDK提供的Timer类,允许调度一个TimerTask任务。Timer位于java.util包下,其内部包含且仅包含一个后台线程(TimeThread)对多个业务任务(TimeTask)进行定时定频率的调度。

schedule的四种用法和scheduleAtFixedRate的两种用法:

public void schedule(TimerTask task, long delay);

public void schedule(TimerTask task, Date time);

public void schedule(TimerTask task, long delay, long period);

public void schedule(TimerTask task, Date firstTime, long period);

public void scheduleAtFiexRate(TimerTask task, long delay, long period);

public void scheduleAtFiexRate(TimerTask task, Date firstTime, long period);

参数说明:

task:所要执行的任务,需要实现TimeTask的run()方法

time/firstTime:首次执行任务的时间

period:周期性执行Task的时间间隔,单位是毫秒

delay:执行task任务前的延时时间,单位是毫秒

很显然,通过上述的描述,我们可以实现:

延迟多久后执行一次任务

指定时间执行一次任务

延迟一段时间,并周期性执行任务

指定时间,并周期性执行任务

代码示例

编写MyTimerTask任务类,用以表示具体需要执行的任务:

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.TimerTask;

import java.util.concurrent.atomic.AtomicInteger;

public class MyTimerTask extends TimerTask {

/**

* The action to be performed by this timer task.

*/

private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

private AtomicInteger count = new AtomicInteger(0);

public void run() {

System.out.println("执行时间:" + FORMAT.format(this.scheduledExecutionTime()));

count.incrementAndGet();

if (count.get() == 10) {

this.cancel();

System.out.println("达到预定执行次数,取消执行计划");

}

}

}

编写TimerDemo类,为需要调度的任务设置调度运行参数:

package com.netease.scaffold.task;

import java.util.Date;

import java.util.Timer;

public class TimerDemo {

public static void main(String[] args) {

// 创建定时器

Timer timer = new Timer();

// 添加调度任务

// schedule(TimerTask task, Date time); 特定时间 time 执行

// timer.schedule(new MyTimerTask(), new Date(System.currentTimeMillis() + 1000));

// schedule(TimerTask task, long delay); //延迟 delay毫秒 执行 task

// timer.schedule(new MyTimerTask(), 1000);

// schedule(TimerTask task, long delay, long period) 延迟 delay毫秒 执行并每隔 period毫秒 执行一次

// timer.schedule(new MyTimerTask(), 1000, 5000);

// schedule(TimerTask task, Date time, long period); 特定时间 time 执行并每隔 period毫秒 执行一次

timer.schedule(new MyTimerTask(), new Date(System.currentTimeMillis() + 1000), 1000);

}

}

测试结果

执行时间:2018-12-25 11:58:00

执行时间:2018-12-25 11:58:01

执行时间:2018-12-25 11:58:02

执行时间:2018-12-25 11:58:03

执行时间:2018-12-25 11:58:04

执行时间:2018-12-25 11:58:05

执行时间:2018-12-25 11:58:06

执行时间:2018-12-25 11:58:07

执行时间:2018-12-25 11:58:08

执行时间:2018-12-25 11:58:09

达到预定执行次数,取消执行计划

不难发现,输出是在当前执行时刻延迟1秒后开始执行的,后面每隔1秒执行一次。达到计划的执行次数之后,取消执行计划。

点评

思考1:如果time/firstTime指定的时间,在当前时间之前,会发生什么呢?

在时间等于或者超过time/firstTime的时候,会执行task!也就是说,如果time/firstTime指定的时间在当前时间之前,就会立即得到执行。

思考2:schedule和scheduleAtFixedRate有什么区别?

scheduleAtFixedRate:每次执行时间为上一次任务开始起向后推一个period间隔,也就是说下次执行时间相对于上一次任务开始的时间点,因此执行时间不会延后,但是存在任务并发执行的问题。

schedule:每次执行时间为上一次任务结束后推一个period间隔,也就是说下次执行时间相对于上一次任务结束的时间点,因此执行时间会不断延后。

思考3:如果执行task发生异常,是否会影响其他task的定时调度?

如果TimeTask抛出RuntimeException,那么Timer会停止所有任务的运行!

思考4:Timer的一些缺陷?

前面已经提及到Timer背后是一个单线程,因此Timer存在管理并发任务的缺陷:所有任务都是由同一个线程来调度,所有任务都是串行执行,意味着同一时间只能有一个任务得到执行,而前一个任务的延迟或者异常会影响到之后的任务。

其次,Timer的一些调度方式还算比较简单,无法适应实际项目中任务定时调度的复杂度。

这种只适合一些最基础的定时任务,作为玩具使用,在实际的项目开发中一般很少用到,了解即可。

方式二:JDK对定时任务调度的线程池支持:ScheduledExecutorService

由于Timer存在的问题,JDK5之后便提供了基于线程池的定时任务调度ScheduledExecutorService。它的设计理念是每一个被调度的任务都会被线程池中的一个线程去执行,因此任务可以并发执行,而且相互之间不受影响。

编写代码ScheduleExecutorServiceDemo

import java.util.Date;

import java.util.concurrent.Executors;

import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.TimeUnit;

public class ScheduleExecutorServiceDemo implements Runnable {

@Override

public void run() {

System.out.println("执行:" + new Date());

}

public static void main(String[] args) {

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

scheduledExecutorService.scheduleAtFixedRate(new ScheduleExecutorServiceDemo(), 1000, 2000, TimeUnit.MILLISECONDS);

}

}

测试结果

执行:Sun Mar 29 17:09:36 CST 2020

执行:Sun Mar 29 17:09:38 CST 2020

执行:Sun Mar 29 17:09:40 CST 2020

执行:Sun Mar 29 17:09:42 CST 2020

...

方式三:Spring Task

Spring也提供了对于每台机器都执行的定时任务的支持,熟悉Spring的同学都知道Spring一般都是同时支持XML配置和注解配置的方式的,下面我们将分别对这两种方式分别介绍。

XML配置方式

添加配置

编写测试任务类SpringTask

package com.netease.scaffold.task;

import java.text.SimpleDateFormat;

import java.util.Date;

public class SpringTask {

private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

public void show1() {

System.out.println("show1:" + FORMAT.format(new Date()));

}

public void show2() {

System.out.println("show2:" + FORMAT.format(new Date()));

}

}

测试结果

show2:2018-12-05 16:01:03

show2:2018-12-05 16:01:04

show1:2018-12-05 16:01:04

show2:2018-12-05 16:01:05

show2:2018-12-05 16:01:06

show2:2018-12-05 16:01:07

show1:2018-12-05 16:01:07

show2:2018-12-05 16:01:08

show2:2018-12-05 16:01:09

show2:2018-12-05 16:01:10

show1:2018-12-05 16:01:10

show2:2018-12-05 16:01:11

show2:2018-12-05 16:01:12

show2:2018-12-05 16:01:13

show1:2018-12-05 16:01:13

show2:2018-12-05 16:01:14

show2:2018-12-05 16:01:15

show2:2018-12-05 16:01:16

show1:2018-12-05 16:01:16

show2:2018-12-05 16:01:17

注解方式

定时任务启用注解

首先,需要在应用入口类上加上@EnableScheduling注解,表示启用注解扫描方式的定时任务。

测试类任务类SpringAnnoTask

package com.netease.scaffold.task;

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;

import java.util.Date;

/**

* spring执行任务的类

*/

@Component

public class SpringAnnoTask {

private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

@Scheduled(cron = "1-10 * * * * ? ")//每分钟的1-10秒每秒执行一次

public void show1() {

System.out.println("show1:" + FORMAT.format(new Date()));

}

@Scheduled(cron = "0/10 * * * * ? ")//每10秒执行一次

public void show2() {

System.out.println("show2:" + FORMAT.format(new Date()));

}

@Scheduled(fixedRate = 2000)//每两秒执行一次时间

public void show3() {

System.out.println("show3:" + FORMAT.format(new Date()));

}

@Scheduled(fixedDelay = 4000)//每次任务执行完之后的4s后继续执行

public void show4() {

System.out.println("show4:" + FORMAT.format(new Date()));

}

}

测试结果

show2:2018-12-05 16:23:54

show1:2018-12-05 16:23:54

show3:2018-12-05 16:23:54

show2:2018-12-05 16:23:55

show2:2018-12-05 16:23:56

show3:2018-12-05 16:23:56

show4:2018-12-05 16:23:56

show2:2018-12-05 16:23:57

show1:2018-12-05 16:23:57

show2:2018-12-05 16:23:58

show3:2018-12-05 16:23:58

show2:2018-12-05 16:23:59

show2:2018-12-05 16:24:00

show2:2018-12-05 16:24:00

show1:2018-12-05 16:24:00

show3:2018-12-05 16:24:00

show4:2018-12-05 16:24:00

show2:2018-12-05 16:24:01

show1:2018-12-05 16:24:01

show2:2018-12-05 16:24:02

show1:2018-12-05 16:24:02

点评

spring task的特点:

默认单线程同步执行

一个任务执行完上一次之后,才会执行下一次调度

多任务之间按顺序执行,一个任务执行完成之后才会执行另一个任务

多任务并行执行需要设置线程池

全程可以通过注解配置

方式四:Quartz框架

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统,“任务进度管理器”就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。虽然ScheduledExecutorService对Timer进行了线程池的改进,但是依然无法满足复杂的定时任务调度场景。因此OpenSymphony提供了强大的开源任务调度框架:Quartz。Quartz是纯Java实现,而且作为Spring的默认调度框架,由于Quartz的强大的调度功能、灵活的使用方式、还具有分布式集群能力,可以说Quartz出马,可以搞定一切定时任务调度!

Quartz体系结构.png

特点

强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;

灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;

分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。

另外,作为 Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能。

核心元素 :

Quartz有3个核心概念:调度器(Scheduler)、任务(Job&JobDetail)、触发器(Trigger)。(一个任务可以被多个触发器触发,一个触发器只能触发一个任务)

Scheduler: 任务调度器,是实际执行任务调度的控制器。在spring中通过SchedulerFactoryBean封装起来。

Trigger :触发器,用于定义任务调度的时间规则,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比较多,本文主要介绍这种方式。CronTrigger在spring中封装在CronTriggerFactoryBean中。

Calendar:它是一些日历特定时间点的集合。一个trigger可以包含多个Calendar,以便排除或包含某些时间点。

**Job **:任务,是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。实现Job接口的任务,默认是无状态的,若要将Job设置成有状态的,在quartz中是给实现的Job添加@DisallowConcurrentExecution注解(以前是实现StatefulJob接口,现在已被Deprecated),在与spring结合中可以在spring配置文件的job detail中配置concurrent参数。

JobDetail :任务信息,用来描述Job实现类及其它相关的静态信息,如Job名字、关联监听器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean两种实现,如果任务调度只需要执行某个类的某个方法,就可以通过MethodInvokingJobDetailFactoryBean来调用。

注意当Scheduler调度Job时,实际上会通过反射newInstance一个新的Job实例(待调度完毕后销毁掉),同时会把JobExecutionContext传递给Job的execute方法,Job实例通过JobExecutionContext访问到Quartz运行时的环境以及Job本身的明细数据。

JobDataMap可以装载任何可以序列化的数据,存取很方便。需要注意的是JobDetail和Trigger都可以各自关联上JobDataMap。JobDataMap除了可以通过上述代码获取外,还可以在YourJob实现类中,添加相应setter方法获取。

实际上,Quartz在进行调度器初始化的时候,会加载quartz.properties文件进行一些属性的设置,比如Quartz后台线程池的属性(threadCount)、作业存储设置等。它会先从工程中找,如果找不到那么就是用quartz.jar中的默认的quartz.properties文件。

Quartz存在监听器的概念,比如任务执行前后、任务的添加等,可以方便实现任务的监控。

Trigger触发器 :

Trigger用来告诉Quartz调度程序什么时候执行,常用的触发器有2种:SimpleTrigger(类似于Timer)、CronTrigger(类似于Linux的Crontab)。

SimpleTrigger :在一个指定时间段内执行一次作业任务或是在指定时间间隔内执行多次作业任务;

CronTrigger :基于日历的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间,比SimpleTrigger更常用。

引入依赖包

commons-collections

commons-collections

3.2.1

org.springframework

spring-context-support

4.3.11.RELEASE

org.quartz-scheduler

quartz

2.2.1

添加配置信息

在Spring的配置文件ApplicationContext.xml添加如下的信息:

class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

定时任务

编写测试定时任务测试代码SimpleTriggerJob:

package com.netease.scaffold.task;

import java.util.Map;

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

public class SimpleTriggerJob implements Job {

@Override

public void execute(JobExecutionContext context) throws JobExecutionException {

Map properties = context.getMergedJobDataMap();

System.out.println("Hello World!");

System.out.println("Previous Fire Time: " + context.getPreviousFireTime());//上次触发任务的时间

System.out.println("Current Fire Time: " + context.getFireTime());//当前触发时间

System.out.println("Next Fire Time: " + context.getNextFireTime());//下次触发时间

System.out.println(properties.get("triggerMessage1"));

System.out.println(properties.get("triggerMessage2"));

System.out.println();

}

}

编写测试定时任务测试代码CronTriggerJob:

package com.netease.scaffold.task;

import java.util.Date;

public class CronTriggerJob {

private static Integer counter = 0;

private static String taskName;

public static String getTaskName() {

return taskName;

}

public static void setTaskName(String taskName) {

CronTriggerJob.taskName = taskName;

}

protected void execute() {

long ms = System.currentTimeMillis();

System.out.println("taskName:" + taskName);

System.out.println("\t\t" + new Date(ms));

System.out.println("(" + counter++ + ")");

}

}

测试结果

测试输出内容每秒执行一次输出,跟预期结果一致。

(17)

2018-12-05 13:14:41,000 [DEBUG] org.quartz.core.JobRunShell - Calling execute on job DEFAULT.springQtzJobMethod

2018-12-05 13:14:41,000 [DEBUG] org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers

taskName:myTaskName

Wed Dec 05 13:14:41 CST 2018

(18)

2018-12-05 13:14:42,000 [DEBUG] org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers

2018-12-05 13:14:42,000 [DEBUG] org.quartz.core.JobRunShell - Calling execute on job DEFAULT.springQtzJobMethod

taskName:myTaskName

Wed Dec 05 13:14:42 CST 2018

(19)

2018-12-05 13:14:42,033 [DEBUG] org.quartz.core.JobRunShell - Calling execute on job DEFAULT.simpleTriggerJob

Hello World!

2018-12-05 13:14:42,033 [DEBUG] org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers

Previous Fire Time: Wed Dec 05 13:14:39 CST 2018

Current Fire Time: Wed Dec 05 13:14:42 CST 2018

Next Fire Time: Wed Dec 05 13:14:45 CST 2018

Job Message In JobDetail

Job Message From Trigger

使用原生Quartz

编写QuartzDemo

import org.quartz.*;

import org.quartz.impl.StdSchedulerFactory;

public class QuartzDemo {

public static void main(String[] args) throws SchedulerException {

// 定义执行任务

JobDetail jobDetail = JobBuilder.newJob(SimpleTriggerJob.class)

.withIdentity("myjob", "myGroup")

.usingJobData("triggerMessage1", "louxj424")

.usingJobData("triggerMessage2", "zhangsan")

.build();

// 定义简单执行的触发器:每两秒执行一次,直到永远

// Trigger trigger = TriggerBuilder.newTrigger()

// .withIdentity("triggerName", "triggerGroup")

// .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever())

// .startNow()

// .build();

// 定义cron表示的触发器:每两秒执行一次,直到永远

Trigger trigger = TriggerBuilder.newTrigger()

.withIdentity("triggerName", "triggerGroup")

.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ? *"))

.build();

// 定义调度器

SchedulerFactory schedulerFactory = new StdSchedulerFactory();

Scheduler scheduler = schedulerFactory.getScheduler();

scheduler.scheduleJob(jobDetail, trigger);

scheduler.start();

}

}

测试

Hello World!

Previous Fire Time: null

Current Fire Time: Tue Dec 25 12:02:12 CST 2018

Next Fire Time: Tue Dec 25 12:02:14 CST 2018

louxj424

zhangsan

Hello World!

Previous Fire Time: Tue Dec 25 12:02:12 CST 2018

Current Fire Time: Tue Dec 25 12:02:14 CST 2018

Next Fire Time: Tue Dec 25 12:02:16 CST 2018

louxj424

zhangsan

Hello World!

Previous Fire Time: Tue Dec 25 12:02:14 CST 2018

Current Fire Time: Tue Dec 25 12:02:16 CST 2018

Next Fire Time: Tue Dec 25 12:02:18 CST 2018

louxj424

zhangsan

cron表达式

Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式:

​ (1)Seconds Minutes Hours DayofMonth Month DayofWeek Year

​ (2)Seconds Minutes Hours DayofMonth Month DayofWeek

特殊符号说明

特殊字符

含义

*

表示所有值。例如在分的字段上设置“*”,表示每一分钟都会触发。

?

表示不指定值。使用场景为不需要关心当前设置的这个字段的值。例如,要在每个月的10号出发一个操作,但是不关心是周几,所以需要在周位置设置为“?”,具体设置为“0 0 0 10 * ? *”

-

表示区间。例如,在小时位置设置10-12,表示10,11,12都会触发。

,

表示指定多个值,例如周字段上设置“MON,WEB,FRI”表示周一、周三和周五会触发。

/

用于递增触发。如秒上面设置“5/15”,表示从5秒开始,每增15秒就触发一次,可以计算出来触发的时间依次为(5,20,35,50)。在月上设置“1/3”表示从每月1号开始,每隔三天触发一次。

L

表示最后的意思。在字段上设置,表示当月最后一天(依据当前月份),如果是二月还会根据是否是闰年。在周字段上表示星期六,相当于“7”或者“SAT”。如果在L前面加上数字,则表示该数据的最后一个。例如,在周字段上设置6L,表示本月的最后一个星期五。

W

表示离指定日期最近的那个工作日(周一至周五)。例如在日字段上设置“15W”,表示离每月15号最近的那个工作日触发。如果15号正好是星期六,则最近的是周五14号触发。如果15号是周末,则找最近的下周一16号触发。如果15号正好是在工作日(周一到周五),则就在当天触发。如果指定格式为“1W”,它表示每月1号往后最近的工作日触发。如果1号正好是星期六,则将在下周一也就是3号触发。(注:“W”前只能设置具体的数字,不允许区间)

#

序号,表示每月的第几个周几。例如在周字段上设置“6#3”表示在每月的第三个周五。

字段值说明

字段

是否必填

允许值

允许的特殊字符

0-59

, - * /

0-59

, - * /

小时

0-23

, - * /

1-31

, - * ? / L C

1-12或者JAN-DEC

, - * /

1-7或者SUN-SAT

. - * ? / L #

empty,1970-2099

, - * /

常用表达式举例

序号

表达式

说明

1

0 15 10 * * ? *

每天10点15分触发

2

0 15 10 * * ? 2017

2017年每天10点15分触发

3

0 * 14 * * ?

每天下午的 2点到2点59分每分触发

4

0 0/5 14 * * ?

每天下午的 2点到2点59分(整点开始,每隔5分触发)

5

0 0/5 14,18 * * ?

每天下午的 2点到2点59分、18点到18点59分(整点开始,每隔5分触发)

6

0 0-5 14 * * ?

每天14点到14点5分内每分种触发一次

7

0 15 10 ? * 6L

每月最后一周的星期五的10点15分触发

8

0 15 10 ? * 6#3

每月第三个星期五的10点15分触发

其他工具

我们可以通过一些Cron在线工具非常方便的生成。

点评

Spring Quartz 特点:【按照配置文件时间表达式:准时准点(不延时的时候)】 ---> 配置到spring application.xml上

默认多线程异步执行

一个任务在上一次调度未完成执行,下一次调度时间到时,会另起一个线程开始新的调度。在业务繁忙时,一个任务或许会有多个线程在执行,导致数据处理异常。

单任务同步:配置属性,可以使一个任务的一次调度在未完成时,而不会开启下一次调度

多个任务同时运行,任务之间没有直接的影响,多任务执行的快慢取决于CPU的性能

一个类对应不一样的job方法并可以定义为不一样的job task任务 [看配置文件内容]

推荐使用CronTrigger的触发器的方式,因为该方式无需继承任何父类或者实现任何的接口,对原有功能代码的侵入性比较小,而且可以实现指定时间执行和指定时间间隔执行,使用上几乎没有任何的限制,因此往往在工程代码中,见到的更多的是这种方式实现的定时调度任务。

参考资料

java定时14点30分_单机定时任务的四种基本实现方式相关推荐

  1. AI:2020年6月22日北京智源大会演讲分享之机器感知专题论坛—14:50-15:30吴玺宏教授《一种具身自监督学习框架:面向任何语种语音的音系构建任务》

    AI:2020年6月22日北京智源大会演讲分享之机器感知专题论坛-14:50-15:30吴玺宏教授<一种具身自监督学习框架:面向任何语种语音的音系构建任务> 导读:首先感谢北京智源大会进行 ...

  2. java按钮权限控制_详解Spring Security 中的四种权限控制方式

    Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Se ...

  3. java 按钮 监听_Button的四种监听方式

    Button按钮设置点击的四种监听方式 注:加粗放大的都是改变的代码 1.使用匿名内部类的形式进行设置 使用匿名内部类的形式,直接将需要设置的onClickListener接口对象初始化,内部的onC ...

  4. HIVE的安装配置、mysql的安装、hive创建表、创建分区、修改表等内容、hive beeline使用、HIVE的四种数据导入方式、使用Java代码执行hive的sql命令

    1.上传tar包 这里我上传的是apache-hive-1.2.1-bin.tar.gz 2.解压 mkdir -p /home/tuzq/software/hive/ tar -zxvf apach ...

  5. java中的json_JAVA中的四种JSON解析方式详解

    JAVA中的四种JSON解析方式详解 我们在日常开发中少不了和JSON数据打交道,那么我们来看看JAVA中常用的JSON解析方式. 1.JSON官方 脱离框架使用 2.GSON 3.FastJSON ...

  6. 【小家java】交换两个变量数值的方法(四种方法)

    相关阅读 [小家java]java5新特性(简述十大新特性) 重要一跃 [小家java]java6新特性(简述十大新特性) 鸡肋升级 [小家java]java7新特性(简述八大新特性) 不温不火 [小 ...

  7. java分布式_分布式锁的四种JAVA实现方式

    前言 作为这一段时间学习分布式锁的总结,本文总结了四种Java分布式锁的实现方式,简单编写了代码,进行模拟实现.相关代码存放在我的github仓库. 为什么要用锁 系统内,有多个消费者,需要对同一共享 ...

  8. 计算机网络中什么叫总衰耗_计算机网络中的四种延迟分别是什么?

    展开全部 计算e69da5e887aa62616964757a686964616f31333431346365机网络中的四种延迟分别是:节点处理延迟 .排队延迟.发送延迟.传播延迟. 1.节点处理延迟 ...

  9. android 按照星期 时间 定时_Spring Boot实现定时任务的四种方式

    点击上方Java学习指南关注公众号 每天阅读Java干货文章 定时任务实现的几种方式: Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.Time ...

最新文章

  1. ucos-iii串口用信号量及环形队列中断发送,用内建消息队列中断接收
  2. Isometric Game 及译法漫谈
  3. 郑可迪 : 培养数据思维,投身电力大数据领域研究 | 提升之路系列(一)
  4. mysql error code 145,MYSQL 错误#145解决方法
  5. Centos7 install Openstack - (第三节)添加镜像服务(Glance)
  6. java学习(125):简单异常处理
  7. Mybatis 中更新方法: updateByPrimaryKeySelective() 和 updateByPrimaryKey() 的区别
  8. 小米10pro第二个摄像头下面_小米10至尊纪念版、小米10 Pro对比评测:至尊版“至尊”在哪里?...
  9. 一款不错的SpringCloud 脚手架项目
  10. 系统功能调用DOS中断INT 21H功能包单字符输入01单字符输出02字符串输入0a字符串输出09
  11. CSS3中很容易混淆的transform,translate,transition。如何去区分,以及综合写法。
  12. k3导入账套_金蝶k3凭证导入导出的操作方法金蝶k3操作指南
  13. 惠普服务器做虚拟化,节省成本立竿见影 惠普虚拟化技术详解
  14. 测试自己移动速度的软件,鼠标灵敏度测试检测工具 测试鼠标的灵敏度与移动速度...
  15. 安装mysql过程中出现无法找到入口,无法定位程序输入点fesetround于动态链接库
  16. php输出跳转下一页,tp5页面输出时,搜索后跳转下一页的处理
  17. 使用v-show v-if 设置元素显示和隐藏
  18. SRAM、PSRAM、SPI FLASH初步认识
  19. 对百度Bingo算法的猜测
  20. Android自定义ViewPager图片指示器,兼容实现底部横线指示器

热门文章

  1. WGS84 CGCS2000 北京54 西安80 大地坐标精确转换
  2. python from import什么意思_Python import与from import使用及区别介绍
  3. 电大2019计算机试题及答案实操题,2019电大计算机WORD统考题库及答案.doc
  4. 宁波三中机器人_【重磅】全国机器人奥林匹克大赛冠军,在三中!
  5. Scratch软件编程等级考试三级——20200620
  6. Qt编写物联网管理平台26-组态设计
  7. solo π环境搭建
  8. [work] shell从字符串中提取数字
  9. 【项目管理】提升项目团队绩效的关键
  10. 海康威视iVMS综合安防系统任意文件上传漏洞复现(0day)