1. 程式人生 > 實用技巧 >SpringBoot(一) 多執行緒與非同步

SpringBoot(一) 多執行緒與非同步

多執行緒與非同步

非同步是目的,而多執行緒是實現這個目的的方法。

1 Java J.U.C執行緒排程

JDK 1.5新增的java.util.concurrent包,增加了併發程式設計的很多類。

Executor

定義了方法execute(),用來執行一個任務

public interface Executor {
    void execute(Runnable command);
}
ExecutorService

提供了生命週期管理的方法。

submit()

shutdown()

invokeAll()

invokeAny()

ThreadPoolExecutor

Java提供的執行緒池類。可自定義執行緒池大小及執行緒處理機制。

corePoolSize:執行緒池的基本大小,即在沒有任務需要執行的時候執行緒池的大小,並且只有在工作佇列滿了的情況下才會建立超出這個數量的執行緒。

maximumPoolSize:執行緒池中允許的最大執行緒數,執行緒池中的當前執行緒數目不會超過該值。

keepAliveTime:執行緒空閒時間

poolSize表示當前執行緒數,新提交一個任務時的處理流程為:

  • 如果當前執行緒池的執行緒數還沒有達到基本大小(poolSize < corePoolSize),無論是否有空閒的執行緒新增一個執行緒處理新提交的任務

  • 如果當前執行緒池的執行緒數大於或等於基本大小(poolSize >= corePoolSize) 且任務佇列未滿時,就將新提交的任務提交到阻塞佇列排隊,等候處理。

  • 如果當前執行緒池的執行緒數大於或等於基本大小(poolSize >= corePoolSize) 且任務佇列滿時:

    • 當前poolSize<maximumPoolSize,那麼就新增執行緒來處理任務
    • 當前poolSize=maximumPoolSize,那麼意味著執行緒池的處理能力已經達到了極限,此時需要拒絕新增加的任務。至於如何拒絕處理新增的任務,取決於執行緒池的飽和策略RejectedExecutionHandler。
ScheduledExecutorService

定時排程介面。有4種排程機制:

//帶延遲時間的排程,只執行一次,排程之後可通過Future.get()阻塞直至任務執行完畢
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
//帶延遲時間的排程,只執行一次,排程之後可通過Future.get()阻塞直至任務執行完畢,並且可以獲取執行結果
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

基於執行緒池的定時排程實現。可以用來在給定延時後執行非同步任務或者週期性執行任務。

ScheduledExecutorService4種排程機制的實現。

2 Spring執行緒排程

TaskExecutor

Spring引入了TaskExecutor介面作為頂層介面。繼承至J.U.C的Executor介面。

TaskScheduler

類似於J.U.C中的ScheduledExecutorService,是任務排程的介面。

功能與ScheduledExecutorService差不多,多了一個可傳Trigger物件的schedule()方法,可支援Corn表示式進行任務排程。

ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
ScheduledFuture<?> schedule(Runnable task, Date startTime);
ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period);
ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period);
ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay);
AsyncTaskExecutor

介面中提供submit()方法執行任務。

ThreadPoolTaskExecutor

Spring提供的執行緒池類。與Java提供的ThreadPoolExecutor執行緒池類類似,可以配置執行緒池屬性及拒絕策略。

處理流程與ThreadPoolExecutor一樣。

ThreadPoolTaskScheduler

實現TaskScheduler介面,實現了其介面的排程機制。與Java中的ScheduledThreadPoolExecutor類類似。

多了一個可傳Trigger物件的schedule()方法,可支援Corn表示式進行任務排程。

3 Spring非同步操作

使用@Async註解

標註了@Async註解的方法,稱之為非同步方法。這些方法將在執行的時候,將會在獨立的執行緒(執行緒池中獲取)中被執行,呼叫者無需等待它的完成,即可繼續其他的操作。

@Configuration
@EnableAsync
public class AsyncConfig {
    
}
@Async
public void downloadOrder() throws InterruptedException {
    System.out.println("執行開始時間為:" + LocalDateTime.now() + "執行緒為:" + Thread.currentThread().getName());
    Thread.sleep(10000);
    System.out.println("執行結束時間為:" + LocalDateTime.now() + "執行緒為:" + Thread.currentThread().getName());
}

@Async標註的方法,在同一個類中呼叫,無效

4 Spring定時任務的幾個實現

4.1 基於@Scheduled註解
@Configuration      //標記為配置類
@EnableScheduling   //開啟定時任務,可以放到啟動類中
public class OrderScheduler {
    //定時任務執行週期,採用Corn表示式
    @Scheduled(cron = "0/5 * * * * ?")
    public void downloadOrder() {
        System.out.println("執行開始時間為:" + LocalDateTime.now() + "執行緒為:" + Thread.currentThread().getName());
        Thread.sleep(10000);
        System.out.println("執行結束時間為:" + LocalDateTime.now() + "執行緒為:" + Thread.currentThread().getName());
    }
}
//並不是每5秒執行一次,上次執行的結果會影響到下次執行。這個相當於10s執行一次了。

@Scheduled註解也可以直接指定時間間隔,如:@Scheduled(fixedRate=5000)

實現本質是基於Java中的ScheduledExecutorService類的schedule方法。

基於@Scheduled註解預設為單執行緒的。

標註了@Scheduled的方法,執行時是單執行緒的,也就是說,上次執行的結果會影響到下一次的執行。

4.2 基於SchedulingConfigurer介面
@FunctionalInterface
public interface SchedulingConfigurer {
	void configureTasks(ScheduledTaskRegistrar taskRegistrar);
}
ScheduledTaskRegistrar類

實現SchedulingConfigurer介面,實現configureTasks方法,方法傳入ScheduledTaskRegistrar類物件。

幾種定時任務型別:

  • CronTask:根據Cron表示式執行定時任務
  • TriggerTask:按照Trigger觸發器觸發執行,可以動態改變定時任務的執行
  • FixedRateTask:固定速度執行
  • FixedDelayTask:固定延遲執行
@EnableScheduling //啟動類,設定定時任務開關
@SpringBootApplication
public class MatrixApplication {

    public static void main(String[] args) {
        SpringApplication.run(MatrixApplication.class, args);
    }

}
@Component
public class TestScheduler implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.addTriggerTask(() -> System.out.println("執行時間:" + LocalDateTime.now()), new CronTrigger("0/2 * * * * ?"));
    }
}

SchedulingConfigurer配置類比@Scheduled註解多了一個TriggerTask,更加靈活,其他的差不多。

4.3 基於註解設定多執行緒定時任務

@Scheduled註解預設為單執行緒的,我們可以使用@Async註解啟用多執行緒。

@Configuration      //標記為配置類
@EnableAsync        //開啟多執行緒
@EnableScheduling   //開啟定時任務,可以放到啟動類中
public class OrderScheduler {
    //定時任務執行週期,採用Corn表示式
    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void downloadOrder() {
        System.out.println("執行開始時間為:" + LocalDateTime.now() + "執行緒為:" + Thread.currentThread().getName());
        Thread.sleep(10000);
        System.out.println("執行結束時間為:" + LocalDateTime.now() + "執行緒為:" + Thread.currentThread().getName());
    }
}

同一個任務的每一次定時任務執行,都會開闢一個新的執行緒,不會影響到下一次任務的執行。

4.4 ThreadPoolTaskScheduler

利用執行緒池實現任務排程。

@Resource
private ThreadPoolTaskScheduler threadPoolTaskScheduler;

@GetMapping("/start")
public void test() {
    threadPoolTaskScheduler.schedule(() -> System.out.println("第" + i + "執行時間:" + LocalDateTime.now()), new CronTrigger("0/2 * * * * ?"));
}
4.5 整合Quartz

定時任務內部的方法可以結合@Async註解使用達到多執行緒的目的

參考文章
  1. https://www.cnblogs.com/frankyou/p/10135212.html
  2. https://blog.csdn.net/weixin_43168010/article/details/97613895