實戰(zhàn)Spring Boot 2.0系列(六) - 單機定時任務(wù)的幾種實現(xiàn)

前言

定時任務(wù) 一般會存在 中大型企業(yè)級 項目中,為了減少 服務(wù)器數(shù)據(jù)庫 的壓力,往往會以 定時任務(wù) 的方式去完成某些業(yè)務(wù)邏輯。

常見的就是 金融服務(wù)系統(tǒng) 推送回調(diào),一般支付系統(tǒng)訂單在沒有收到成功的回調(diào)返回內(nèi)容時會 持續(xù)性的回調(diào),這種回調(diào)一般都是 定時任務(wù) 來完成。

還有就是 報表的生成,我們一般會在客戶 訪問量小 時完成這個操作,也可以采用 定時任務(wù) 來完成。

image

正文

定時任務(wù)的幾種方式

Timer

這是 Java 自帶的 java.util.Timer 類,這個類允許調(diào)度一個名為 java.util.TimerTask 任務(wù)。使用這種方式可以讓你的程序按照某一個 頻度 執(zhí)行,但不能在 指定時間 運行?,F(xiàn)在一般用的較少。

ScheduledExecutorService

JDK 自帶的一個類,是基于 線程池 設(shè)計的定時任務(wù)類,每個 調(diào)度任務(wù) 都會分配到 線程池 中的一個 線程 去執(zhí)行。也就是說,任務(wù)是 并發(fā)執(zhí)行,互不影響的。

Spring Task

Spring 3.0 以后自帶的 Task,支持 多線程 調(diào)度,可以將它看成一個 輕量級Quartz,而且使用起來比 Quartz 簡單許多,但是適用于 單節(jié)點定時任務(wù)調(diào)度

Quartz

這是一個 功能比較強大 的的調(diào)度器,可以讓你的程序在指定時間執(zhí)行,也可以按照某一個頻度執(zhí)行,配置起來 稍顯復(fù)雜。Quartz 功能強大,可以結(jié)合 數(shù)據(jù)庫持久化,進(jìn)行 分布式任務(wù)延時調(diào)度。

Cron表達(dá)式簡介

Cron 表達(dá)式是一個字符串,字符串以 56空格 隔開,分為 67,每一個域代表一個含義,Cron 有如下兩種語法格式:

  1. Seconds Minutes Hours DayofMonth Month DayofWeek Year
  2. Seconds Minutes Hours DayofMonth Month DayofWeek

每個域?qū)?yīng)的含義、域值范圍和特殊表示符,從左到右依次如下:

字段 允許值 允許的特殊字符
0-59 , - * /
0-59 , - * /
小時 0-23 , - * /
日期 1-31 , - * / L W C
月份 1-12 或者 JAN-DEC , - * /
星期 1-7 或者 SUN-SAT , - * / L C #
年(可選) 留空, 1970-2099 , - * /

如上面的表達(dá)式所示:

  • ""字符: 被用來指定所有的值。如:在分鐘的字段域里表示"每分鐘"。

  • "-"字符: 被用來指定一個范圍。如:"10-12" 在小時域意味著 "10點、11點、12點"。

  • ","字符: 被用來指定另外的值。如:"MON,WED,FRI" 在星期域里表示 "星期一、星期三、星期五"。

  • "?"字符: 只在日期域和星期域中使用。它被用來指定"非明確的值"。當(dāng)你需要通過在這兩個域中的一個來指定一些東西的時候,它是有用的??聪旅娴睦幽憔蜁靼?。

  • "L"字符: 指定在月或者星期中的某天(最后一天)。即 "Last" 的縮寫。但是在星期和月中 "L" 表示不同的意思,如:在月子段中 "L" 指月份的最后一天 - 1月31日,2月28日。

    • 如果在星期字段中則簡單的表示為 "7" 或者 "SAT" 字符。
    • 如果在星期字段中在某個 value 值得后面,則表示 "某月的最后一個星期value",如 "6L" 表示某月的最后一個星期五。
  • "W"字符: 只能用在月份字段中,該字段指定了離指定日期最近的那個星期日。

  • "#"字符: 只能用在星期字段,該字段指定了第幾個星期 value 在某月中

每一個元素都可以顯式地規(guī)定一個值(如 6),一個區(qū)間(如 9-12),一個列表(如 9,11,13)或一個通配符(如 *)。"月份中的日期""星期中的日期" 這兩個元素是 互斥的,因此應(yīng)該通過設(shè)置一個 問號?)來表明你不想設(shè)置的那個字段。下表顯示了一些 cron 表達(dá)式的 例子 和它們的意義:

表達(dá)式 意義
"0 0 12 * * ?" 每天中午12點觸發(fā)
"0 15 10 ? * *" 每天上午10:15觸發(fā)
"0 15 10 * * ?" 每天上午10:15觸發(fā)
"0 15 10 * * ? *" 每天上午10:15觸發(fā)
"0 15 10 * * ? 2005" 2005年的每天上午10:15觸發(fā)
"0 * 14 * * ?" 在每天下午2點到下午2:59期間的每1分鐘觸發(fā)
"0 0/5 14 * * ?" 在每天下午2點到下午2:55期間的每5分鐘觸發(fā)
"0 0/5 14,18 * * ?" 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發(fā)
"0 0-5 14 * * ?" 在每天下午2點到下午2:05期間的每1分鐘觸發(fā)
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44觸發(fā)
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15觸發(fā)
"0 15 10 15 * ?" 每月15日上午10:15觸發(fā)
"0 15 10 L * ?" 每月最后一日的上午10:15觸發(fā)
"0 15 10 ? * 6L" 每月的最后一個星期五上午10:15觸發(fā)
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一個星期五上午10:15觸發(fā)
"0 15 10 ? * 6#3" 每月的第三個星期五上午10:15觸發(fā)
0 6 * * * 每天早上6點
0 /2 * * 每兩個小時
0 23-7/2,8 * * * 晚上11點到早上8點之間每兩個小時,早上八點
0 11 4 * 1-3 每個月的4號和每個禮拜的禮拜一到禮拜三的早上11點
0 4 1 1 * 1月1日早上4點

環(huán)境準(zhǔn)備

配置gradle依賴

利用 Spring Initializer 創(chuàng)建一個 gradle 項目 spring-boot-scheduler-task-management,創(chuàng)建時添加相關(guān)依賴。得到的初始 build.gradle 如下:

buildscript {
    ext {
        springBootVersion = '2.0.3.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'io.ostenant.springboot.sample'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Spring Boot 入口類上配置 @EnableScheduling 注解開啟 Spring 自帶的定時處理功能。

@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

配置Timer任務(wù)

這個 API 目前在項目中很少用,直接給出示例代碼。具體的介紹可以查看 API。Timer 的內(nèi)部只有 一個線程,如果有 多個任務(wù) 的話就會 順序執(zhí)行,這樣任務(wù)的 延遲時間循環(huán)時間 就會出現(xiàn)問題。

TimerService.java

public class TimerService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TimerService.class);
    private AtomicLong counter = new AtomicLong();

    public void schedule() {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                long count = counter.incrementAndGet();
                LOGGER.info("Schedule timerTask {} times", count);
            }
        };
        Timer timer = new Timer();
        timer.schedule(timerTask, 1000L, 10 * 1000L;
    }
}

上面的代碼定義了一個 TimerTask,在 TimerTask 中累加 執(zhí)行次數(shù),并通過 slf4j 進(jìn)行打印 (自帶執(zhí)行時間)。然后通過 Timer 調(diào)度工具類調(diào)度 TimerTask 任務(wù),設(shè)置 初始化延遲時間1s定時執(zhí)行間隔10s,測試代碼如下:

public static void main(String[] args) {
    TimerService timerService = new TimerService();
    timerService.schedule();
}

觀察測試結(jié)果,能夠發(fā)現(xiàn) TimerTask 配置的任務(wù)每隔 10s 被執(zhí)行了一次,執(zhí)行線程默認(rèn)都是 Timer-0 這個線程。

17:48:18.731 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 1 times
17:48:28.730 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 2 times
17:48:38.736 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 3 times
17:48:48.738 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 4 times
17:48:58.743 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 5 times

配置ScheduledExecutorService任務(wù)

ScheduledExecutorService延時執(zhí)行 的線程池,對于 多線程 環(huán)境下的 定時任務(wù),推薦用 ScheduledExecutorService 代替 Timer 定時器。

創(chuàng)建一個線程數(shù)量為 4任務(wù)線程池,同一時刻并向它提交 4 個定時任務(wù),用于測試延時任務(wù)的 并發(fā)處理。執(zhí)行 ScheduledExecutorServicescheduleWithFixedDelay() 方法,設(shè)置任務(wù)線程池的 初始任務(wù)延遲時間2 秒,并在上一次 執(zhí)行完畢時間點 之后 10 秒再執(zhí)行下一次任務(wù)。

public void scheduleWithFixedDelay() {
    ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4);
    for (int i = 0; i < 4; i++) {
        scheduledExecutor.scheduleWithFixedDelay(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(10 * 1000L);
            } catch (InterruptedException e) {
                LOGGER.error("Interrupted exception", e);
            }
            long count = counter.incrementAndGet();
            LOGGER.info("Schedule executor {} times with fixed delay", count);
        }, 2000L, 10 * 1000L, TimeUnit.MILLISECONDS);
    }
    LOGGER.info("Start to schedule");
}

測試結(jié)果如下,我們可以發(fā)現(xiàn)每隔 20 秒的時間間隔,就會有 4 個定時任務(wù)同時執(zhí)行。因為在任務(wù)線程池初始化時,我們同時向線程池提交了 4 個任務(wù),這 四個任務(wù) 會完全利用線程池中的 4 個線程進(jìn)行任務(wù)執(zhí)行。

20 秒是怎么來的?首先每個任務(wù)的 時間間隔 設(shè)置為 10 秒。其次因為采用的是 withFixedDelay 策略,即當(dāng)前任務(wù)執(zhí)行的 結(jié)束時間,作為下次延時任務(wù)的 開始計時節(jié)點,并且每個任務(wù)在執(zhí)行過程中睡眠了 10 秒的時間,累計起來就是 20 秒的時間。

19:42:02.444 [main] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Start to schedule
19:42:14.449 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 3 times with fixed delay
19:42:14.449 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 1 times with fixed delay
19:42:14.449 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 2 times with fixed delay
19:42:14.449 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 4 times with fixed delay
19:42:34.458 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 7 times with fixed delay
19:42:34.458 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 5 times with fixed delay
19:42:34.458 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 8 times with fixed delay
19:42:34.458 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 6 times with fixed delay

創(chuàng)建一個線程數(shù)量為 4任務(wù)線程池,同一時刻并向它提交 4 個定時任務(wù),用于測試延時任務(wù)的 并發(fā)處理。每個任務(wù)分別執(zhí)行 ScheduledExecutorServicescheduleAtFixedRate() 方法,設(shè)置任務(wù)線程池的 初始任務(wù)延遲時間2 秒,并在上一次 開始執(zhí)行時間點 之后 10 秒再執(zhí)行下一次任務(wù)。

public void scheduleAtFixedRate() {
    ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4);
    for (int i = 0; i < 4; i++) {
        scheduledExecutor.scheduleAtFixedRate(() -> {
            long count = counter.incrementAndGet();
            LOGGER.info("Schedule executor {} times at fixed rate", count);
        }, 2000L, 10 * 1000L, TimeUnit.MILLISECONDS);
    }
    LOGGER.info("Start to schedule");
}

測試結(jié)果如下,我們可以發(fā)現(xiàn)每隔 10 秒的時間間隔,就會有 4 個定時任務(wù)同時執(zhí)行,因為在任務(wù)線程池初始化時,我們同時向線程池提交了 4 個任務(wù),這 四個任務(wù) 會完全利用線程池中的 4 個線程進(jìn)行任務(wù)執(zhí)行。

19:31:46.837 [main] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Start to schedule
19:31:48.840 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 1 times at fixed rate
19:31:48.840 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 3 times at fixed rate
19:31:48.840 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 2 times at fixed rate
19:31:48.840 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 4 times at fixed rate
19:31:58.839 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 6 times at fixed rate
19:31:58.840 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 8 times at fixed rate
19:31:58.839 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 7 times at fixed rate
19:31:58.839 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 5 times at fixed rate

配置Spring Task任務(wù)

Spring 提供了 @Scheduled 注解來實現(xiàn) 定時任務(wù),@Scheduled 參數(shù)可以接受 兩種 定時的設(shè)置,一種是我們常用的 格林時間表達(dá)式 cron = "*/10 * * * * *",另一種是 fixedRate = 10 * 1000L,兩種都表示每隔 10 秒執(zhí)行一次目標(biāo)任務(wù)。

參數(shù)說明:

  • @Scheduled(fixedRate = 10 * 1000L):上一次 開始執(zhí)行時間點 之后 10 秒再執(zhí)行。
@Scheduled(fixedRate = 10 * 1000L)
public void scheduleAtFixedRate() throws Exception {
    long count = counter.incrementAndGet();
    LOGGER.info("Schedule executor {} times at fixed rate", count);
}
  • @Scheduled(fixedDelay = 10 * 1000L):上一次 執(zhí)行完畢時間點 之后 10 秒再執(zhí)行。
@Scheduled(fixedDelay = 10 * 1000L)
public void scheduleWithFixedDelay() throws Exception {
    try {
        TimeUnit.MILLISECONDS.sleep(10 * 1000L);
    } catch (InterruptedException e) {
        LOGGER.error("Interrupted exception", e);
    }
    long count = counter.incrementAndGet();
    LOGGER.info("Schedule executor {} times with fixed delay", count);
}
  • @Scheduled(initialDelay = 2000L, fixedRate = 10 * 1000L):第一次延遲 2 秒后執(zhí)行,之后按 fixedRate 的規(guī)則每 10 秒執(zhí)行一次。
@Scheduled(initialDelay = 2000L, fixedDelay = 10 * 1000L)
public void scheduleWithinitialDelayAndFixedDelay() throws Exception {
    try {
        TimeUnit.MILLISECONDS.sleep(10 * 1000L);
    } catch (InterruptedException e) {
        LOGGER.error("Interrupted exception", e);
    }
    long count = counter.incrementAndGet();
    LOGGER.info("Schedule executor {} times with fixed delay", count);
}
  • @Scheduled(cron = "0/10 * * * * *"):根據(jù) cron 表達(dá)式定義,每隔 10 秒執(zhí)行一次。
@Scheduled(cron = "0/10 * * * * *")
public void scheduleWithCronExpression() throws Exception {
    long count = counter.incrementAndGet();
    LOGGER.info("Schedule executor {} times with ", count);
}

完整的代碼如下:

SpringTaskService.java

@Component
public class SpringTaskService {
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringTaskService.class);
    private AtomicLong counter = new AtomicLong();

    @Scheduled(fixedDelay = 10 * 1000L)
    public void scheduleWithFixedDelay() throws Exception {
        try {
            TimeUnit.MILLISECONDS.sleep(10 * 1000L);
        } catch (InterruptedException e) {
            LOGGER.error("Interrupted exception", e);
        }
        long count = counter.incrementAndGet();
        LOGGER.info("Schedule executor {} times with fixed delay", count);
    }

    @Scheduled(initialDelay = 2000L, fixedDelay = 10 * 1000L)
    public void scheduleWithinitialDelayAndFixedDelay() throws Exception {
        try {
            TimeUnit.MILLISECONDS.sleep(10 * 1000L);
        } catch (InterruptedException e) {
            LOGGER.error("Interrupted exception", e);
        }
        long count = counter.incrementAndGet();
        LOGGER.info("Schedule executor {} times with fixed delay", count);
    }

    @Scheduled(fixedRate = 10 * 1000L)
    public void scheduleAtFixedRate() throws Exception {
        long count = counter.incrementAndGet();
        LOGGER.info("Schedule executor {} times at fixed rate", count);
    }

    @Scheduled(cron = "0/10 * * * * *")
    public void scheduleWithCronExpression() throws Exception {
        long count = counter.incrementAndGet();
        LOGGER.info("Schedule executor {} times with ", count);
    }
}

查看日志,任務(wù)每 20 秒的時間間隔執(zhí)行一次。每次定時任務(wù)在上次 執(zhí)行完畢時間點 之后 10 秒再執(zhí)行,在任務(wù)中設(shè)置 睡眠時間10 秒。這里只驗證了 @Scheduled(initialDelay = 2000L, fixedDelay = 10 * 1000L)。

2018-06-25 18:00:53.051  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 1 times with fixed delay
2018-06-25 18:01:13.056  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 2 times with fixed delay
2018-06-25 18:01:33.061  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 3 times with fixed delay
2018-06-25 18:01:53.071  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 4 times with fixed delay
2018-06-25 18:02:13.079  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 5 times with fixed delay

配置任務(wù)線程池

上述配置都是基于 單線程 的任務(wù)調(diào)度,如何引入 多線程 提高 延時任務(wù)并發(fā)處理 能力?

Spring Boot 提供了一個 SchedulingConfigurer 配置接口。我們通過 ScheduleConfig 配置文件實現(xiàn) ScheduleConfiguration 接口,并重寫 configureTasks() 方法,向 ScheduledTaskRegistrar 注冊一個 ThreadPoolTaskScheduler 任務(wù)線程對象即可。

@Configuration
public class ScheduleConfiguration implements SchedulingConfigurer {
    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleConfiguration.class);

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setTaskScheduler(taskScheduler());
    }

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(4);
        taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        taskScheduler.setThreadNamePrefix("schedule");
        taskScheduler.setRemoveOnCancelPolicy(true);
        taskScheduler.setErrorHandler(t -> LOGGER.error("Error occurs", t));
        return taskScheduler;
    }
}

啟動 Spring Boot 引用,上面 SpringTaskService 配置的 4 個定時任務(wù)會同時生效。

2018-06-20 20:37:50.746  INFO 8142 --- [      schedule1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 1 times at fixed rate
2018-06-20 20:38:00.001  INFO 8142 --- [      schedule3] i.o.s.sample.spring.SpringTaskService    : Schedule executor 2 times with 
2018-06-20 20:38:00.751  INFO 8142 --- [      schedule1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 3 times at fixed rate
2018-06-20 20:38:02.748  INFO 8142 --- [      schedule2] i.o.s.sample.spring.SpringTaskService    : Schedule executor 4 times with fixed delay
2018-06-20 20:38:10.005  INFO 8142 --- [      schedule4] i.o.s.sample.spring.SpringTaskService    : Schedule executor 5 times with 
2018-06-20 20:38:10.747  INFO 8142 --- [      schedule3] i.o.s.sample.spring.SpringTaskService    : Schedule executor 6 times at fixed rate
2018-06-20 20:38:20.002  INFO 8142 --- [      schedule2] i.o.s.sample.spring.SpringTaskService    : Schedule executor 7 times with 
2018-06-20 20:38:20.747  INFO 8142 --- [      schedule4] i.o.s.sample.spring.SpringTaskService    : Schedule executor 8 times at fixed rate

觀察日志,線程名前綴schedule,可以發(fā)現(xiàn) Spring Task@Scheduled 注解配置的 4 個任務(wù),分發(fā)給我們配置的 ThreadPoolTaskScheduler 中的 4 個線程并發(fā)執(zhí)行。

小結(jié)

本文介紹了基于單節(jié)點的定時任務(wù)調(diào)度及實現(xiàn),包括 JDK 原生的 TimerScheduledExecutorService,以及 Spring 3.0 以后自帶的基于注解的 Spring Task 任務(wù)調(diào)度方式。除此之外,重點闡述了基于 固定延時、固定頻率cron 表達(dá)式 的不同之處,并對 ScheduledExecutorServiceSpring Scheduler線程池并發(fā)處理 進(jìn)行了測試。


歡迎關(guān)注技術(shù)公眾號: 零壹技術(shù)棧

零壹技術(shù)棧

本帳號將持續(xù)分享后端技術(shù)干貨,包括虛擬機基礎(chǔ),多線程編程,高性能框架,異步、緩存和消息中間件,分布式和微服務(wù),架構(gòu)學(xué)習(xí)和進(jìn)階等學(xué)習(xí)資料和文章。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評論 19 139
  • 博客原文 徒手翻譯spring framework 4.2.3官方文檔的第33章,若有翻譯不當(dāng)之處請指正。 定時任...
    rabbitGYK閱讀 5,837評論 4 24
  • 定時任務(wù)實現(xiàn)的幾種方式: 1、Timer:這是java自帶的java.util.Timer類,這個類允許你調(diào)度一個...
    Steven_sunlu閱讀 83,134評論 1 18
  • title: springboot定時任務(wù)copyright: truecategories: springmvc...
    qinhej閱讀 1,173評論 0 0
  • 今天一早惶恐難安,恍夢已時古時舊人,權(quán)傾歸田。一日不時不知原由,登車上路,窗換繩換,猶如舊時之景,感慨笑之。行若風(fēng)...
    涼皮君啊閱讀 520評論 0 0

友情鏈接更多精彩內(nèi)容