Spring Boot ThreadPool - Spring Boot 168 EP 24

Spring Boot ThreadPool – Spring Boot 168 EP 24

Spring Boot ThreadPool

為了降低資源損耗,重用執行緒、減少執行緒建立和切換所帶來的開銷,提供了執行緒連線池管理、設定連線、閒置存活時間等功能, Spring Boot Thread Pool Configuration 增加了範例,並透過 JUnit 5 單元測試來驗證產出結果。

功能簡介

ThreadPool 是一個執行緒池,其原理類似資料庫連接池,池中通常有固定或變動數量的執行緒,每當有任務要執行時,就使用池中的執行緒來處理,執行完成後,該執行緒放回池中,繼續等待下個任務。

檔案目錄

./
   +- build.gradle
       +- src
           +- main
               +- java
                   +- org
                       +- ruoxue
                           +- spring_boot_168
                               +- config
                                   +- ThreadPoolConfig.java  

sysThreadPool

Spring Boot Threading 系統連線池,設定連線池大小、閒置存活時間等。

	@Bean(value = "sysThreadPool")
	public ExecutorService sysThreadPool() {
		String prefix = "SYS-T-";
		ExecutorService executor = new ThreadPoolExecutor(CORE_POOL_SIZE, //
				MAX_POOLSIZE, //
				KEEP_LIVE_TIME, //
				TimeUnit.MILLISECONDS, //
				new SynchronousQueue<>(), //
				new NamedThreadFactory(prefix), //
				new ThreadPoolExecutor.CallerRunsPolicy() //
		);
		log.info("prefix: " + prefix);
		return executor;
	}
	

gameThreadPool

Spring Boot Threading 遊戲連線池,設定連線池大小、閒置存活時間等。

	@Bean(value = "gameThreadPool")
	public ExecutorService gameThreadPool() {
		String prefix = "GAME-T-";
		ExecutorService executor = new ThreadPoolExecutor(CORE_POOL_SIZE, //
				MAX_POOLSIZE, //
				KEEP_LIVE_TIME, //
				TimeUnit.MILLISECONDS, //
				new SynchronousQueue<>(), //
				new NamedThreadFactory(prefix), //
				new ThreadPoolExecutor.CallerRunsPolicy() //
		);
		log.info("prefix: " + prefix);
		return executor;
	}

mqThreadPool

Spring Boot Threading 佇列訊息連線池,設定連線池大小、閒置存活時間等。

	@Bean(value = "mqThreadPool")
	public ExecutorService mqThreadPool() {
		String prefix = "MQ-T-";
		ExecutorService executor = new ThreadPoolExecutor(CORE_POOL_SIZE, //
				MAX_POOLSIZE, //
				KEEP_LIVE_TIME, //
				TimeUnit.MILLISECONDS, //
				new SynchronousQueue<>(), //
				new NamedThreadFactory(prefix), //
				new ThreadPoolExecutor.CallerRunsPolicy() //
		);
		log.info("prefix: " + prefix);
		return executor;
	}

ThreadPoolConfig.java

新增檔案,依照用途,建立多個連線池,設定連線池大小、閒置存活時間等。

ThreadPool Java

  1. sysThreadPool 前綴 SYS-T- 開頭,用於處理系統任務,如:定時統計報表,同步資料等用途。
  2. gameThreadPool 前綴 GAME-T- 開頭,用於處理遊戲任務,如:統計線上人數等用途。
  3. mqThreadPool 前綴 MQ-T- 開頭,用於處理 MQ 任務,如:發送訊息,交換資料等用途
package org.ruoxue.spring_boot_168.config;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.TimeUnit;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import lombok.extern.slf4j.Slf4j;

@Configuration
@Slf4j
public class ThreadPoolConfig {
	public static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors();
	public static final int MAX_POOLSIZE = 50 * CORE_POOL_SIZE;
	public static final long KEEP_LIVE_TIME = 60000L;
	public static final int QUEUE_CAPACITY = 10;

	@Bean(value = "sysThreadPool")
	public ExecutorService sysThreadPool() {
		String prefix = "SYS-T-";
		ExecutorService executor = new ThreadPoolExecutor(CORE_POOL_SIZE, //
				MAX_POOLSIZE, //
				KEEP_LIVE_TIME, //
				TimeUnit.MILLISECONDS, //
				new SynchronousQueue<>(), //
				new NamedThreadFactory(prefix), //
				new CallerRunsPolicy() //
		);
		log.info("prefix: " + prefix);
		return executor;
	}

	@Bean(value = "gameThreadPool")
	public ExecutorService gameThreadPool() {
		String prefix = "GAME-T-";
		ExecutorService executor = new ThreadPoolExecutor(CORE_POOL_SIZE, //
				MAX_POOLSIZE, //
				KEEP_LIVE_TIME, //
				TimeUnit.MILLISECONDS, //
				new SynchronousQueue<>(), //
				new NamedThreadFactory(prefix), //
				new CallerRunsPolicy() //
		);
		log.info("prefix: " + prefix);
		return executor;
	}

	@Bean(value = "mqThreadPool")
	public ExecutorService mqThreadPool() {
		String prefix = "MQ-T-";
		ExecutorService executor = new ThreadPoolExecutor(CORE_POOL_SIZE, //
				MAX_POOLSIZE, //
				KEEP_LIVE_TIME, //
				TimeUnit.MILLISECONDS, //
				new SynchronousQueue<>(), //
				new NamedThreadFactory(prefix), //
				new CallerRunsPolicy() //
		);
		log.info("prefix: " + prefix);
		return executor;
	}

	public static class NamedThreadFactory implements ThreadFactory {
		private final AtomicInteger count = new AtomicInteger();
		private final String prefix;

		public NamedThreadFactory(String prefix) {
			this.prefix = prefix;
		}

		@Override
		public Thread newThread(Runnable r) {
			Thread thread = new Thread(r);
			int c = count.incrementAndGet();
			thread.setName(prefix + "-" + c);
			return thread;
		}
	}
}

單元測試

ThreadPoolConfigTest.java

Multithreading in Java Spring Boot 新增檔案,驗證是否符合預期 。

package org.ruoxue.spring_boot_168.config;

import static org.junit.jupiter.api.Assertions.assertNotNull;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.ruoxue.spring_boot_168.Application;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class)
public class ThreadPoolConfigTest {

	@Autowired
	private ThreadPoolConfig config;

	@Test
	public void config() {
		assertNotNull(config);
	}
}

config

測試方法上點右鍵執行 Run As -> JUnit Test ,查看 console 。

2022-06-17T00:39:10.763+0800 [main] INFO sysThreadPool:32 - prefix: SYS-T- 
2022-06-17T00:39:10.763+0800 [main] INFO sysThreadPool:33 - corePoolSize: 6
2022-06-17T00:39:10.764+0800 [main] INFO sysThreadPool:34 - maximumPoolSize: 300
2022-06-17T00:39:10.767+0800 [main] INFO gameThreadPool:49 - prefix: GAME-T- 
2022-06-17T00:39:10.768+0800 [main] INFO gameThreadPool:50 - corePoolSize: 6
2022-06-17T00:39:10.768+0800 [main] INFO gameThreadPool:51 - maximumPoolSize: 300
2022-06-17T00:39:10.768+0800 [main] INFO mqThreadPool:66 - prefix: MQ-T- 
2022-06-17T00:39:10.769+0800 [main] INFO mqThreadPool:67 - corePoolSize: 6
2022-06-17T00:39:10.769+0800 [main] INFO mqThreadPool:68 - maximumPoolSize: 300

心得分享

Spring Multithread 系統中有許多不同類型的任務,可考慮建立多個 ThreadPool ,來因應不同的任務需求,避免不同類型任務互搶同一池的執行緒 ,當再也無法分配空閒的執行緒時,當下任務可能會發生堵塞等待,或執行失敗等狀況,事前的規劃與分配,將可以避免許多不可預期的狀況。

發佈留言