SpringBoot創(chuàng)建線程池的六種方式小結(jié)
1. 自定義線程池
1.1 示例代碼
/**
* 自定義線程池
* <p>
* 優(yōu)點(diǎn):可以自定義參數(shù)
* </p>
*/
@Test
public void newThreadPoolExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
// 核心線程數(shù)
3,
// 最大線程數(shù)
5,
// 空閑線程最大存活時(shí)間
60L,
// 空閑線程最大存活時(shí)間單位
TimeUnit.SECONDS,
// 等待隊(duì)列及大小
new ArrayBlockingQueue<>(100),
// 創(chuàng)建新線程時(shí)使用的工廠
Executors.defaultThreadFactory(),
// 當(dāng)線程池達(dá)到最大時(shí)的處理策略
// new ThreadPoolExecutor.AbortPolicy() // 拋出RejectedExecutionHandler異常
new ThreadPoolExecutor.CallerRunsPolicy() // 交由調(diào)用者的線程執(zhí)行
// new ThreadPoolExecutor.DiscardOldestPolicy() // 丟掉最早未處理的任務(wù)
// new ThreadPoolExecutor.DiscardPolicy() // 丟掉新提交的任務(wù)
);
// 總共5個(gè)任務(wù)
for (int i = 1; i <= 5; i++) {
int taskIndex = i;
executor.execute(() -> {
log.info("線程 " + Thread.currentThread().getName() + " 正在執(zhí)行任務(wù) " + taskIndex);
// 每個(gè)任務(wù)耗時(shí)1秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
控制臺(tái)打?。?/p>
20:09:50.032 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-1 正在執(zhí)行任務(wù) 1
20:09:50.032 [pool-1-thread-2] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-2 正在執(zhí)行任務(wù) 2
20:09:50.032 [pool-1-thread-3] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-3 正在執(zhí)行任務(wù) 3
20:09:51.038 [pool-1-thread-2] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-2 正在執(zhí)行任務(wù) 5
20:09:51.038 [pool-1-thread-3] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-3 正在執(zhí)行任務(wù) 4
2. 固定長度線程池
2.1 示例代碼
/**
* 固定大小線程池
* <p>
* 優(yōu)點(diǎn):當(dāng)任務(wù)執(zhí)行較快,且任務(wù)較少時(shí)使用方便
* </p>
* <p>
* 風(fēng)險(xiǎn):當(dāng)處理較慢時(shí),等待隊(duì)列的任務(wù)堆積會(huì)導(dǎo)致OOM
* </p>
*/
@Test
public void newFixThreadPool() {
// 3個(gè)固定線程
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 總共5個(gè)任務(wù)
for (int i = 1; i <= 5; i++) {
int taskIndex = i;
executorService.execute(() -> {
log.info("線程 " + Thread.currentThread().getName() + " 正在執(zhí)行任務(wù) " + taskIndex);
// 每個(gè)任務(wù)耗時(shí)1秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executorService.shutdown();
}
控制臺(tái)打?。?/p>
20:16:27.040 [pool-1-thread-2] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-2 正在執(zhí)行任務(wù) 2
20:16:27.040 [pool-1-thread-3] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-3 正在執(zhí)行任務(wù) 3
20:16:27.040 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-1 正在執(zhí)行任務(wù) 1
20:16:28.048 [pool-1-thread-3] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-3 正在執(zhí)行任務(wù) 4
20:16:28.048 [pool-1-thread-2] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-2 正在執(zhí)行任務(wù) 5
前3個(gè)任務(wù)被同時(shí)執(zhí)行,因?yàn)閯偤糜?個(gè)核心線程。后2個(gè)任務(wù)會(huì)被存放到阻塞隊(duì)列,當(dāng)執(zhí)行前3個(gè)任務(wù)的某個(gè)線程空閑時(shí)會(huì)從隊(duì)列中獲取任務(wù)并執(zhí)行。
2.2 源碼剖析
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
該類型線程池的核心線程數(shù)和最大線程數(shù)為指定的參數(shù),空閑線程的存活線程時(shí)間為0毫秒,等待隊(duì)列使用LinkedBlockingQueue,初始化大小為Integer.MAX_VALUE(即:2147483647)。
當(dāng)任務(wù)執(zhí)行較慢時(shí),阻塞隊(duì)列存有大量的任務(wù)等待,這些任務(wù)會(huì)占用大量的內(nèi)存,從而可能導(dǎo)致OOM。
3. 單一線程池
3.1 示例代碼
/**
* 單一線程池
* <p>
* 優(yōu)勢(shì):保存任務(wù)按照提交的順序執(zhí)行
* </p>
* <p>
* 風(fēng)險(xiǎn):當(dāng)處理較慢時(shí),等待隊(duì)列的任務(wù)堆積會(huì)導(dǎo)致OOM
* </p>
*/
@Test
public void newSingleThreadExecutor() {
// 1個(gè)線程
ExecutorService executor = Executors.newSingleThreadExecutor();
// 總共5個(gè)任務(wù)
for (int i = 1; i <= 5; i++) {
int taskIndex = i;
executor.execute(() -> {
log.info("線程 " + Thread.currentThread().getName() + " 正在執(zhí)行任務(wù) " + taskIndex);
// 每個(gè)任務(wù)耗時(shí)1秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
控制臺(tái)打?。?/p>
20:31:04.970 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-1 正在執(zhí)行任務(wù) 1
20:31:05.974 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-1 正在執(zhí)行任務(wù) 2
20:31:06.974 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-1 正在執(zhí)行任務(wù) 3
20:31:07.975 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-1 正在執(zhí)行任務(wù) 4
20:31:08.976 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-1 正在執(zhí)行任務(wù) 5
所有任務(wù)按照提交的順序執(zhí)行。
3.2 源碼剖析
/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
該類型線程池的核心線程數(shù)和最大線程數(shù)都為1,空閑線程的存活線程時(shí)間為0毫秒,等待隊(duì)列使用LinkedBlockingQueue,初始化大小為Integer.MAX_VALUE(即:2147483647)。
當(dāng)任務(wù)執(zhí)行較慢時(shí),阻塞隊(duì)列存有大量的任務(wù)等待,這些任務(wù)會(huì)占用大量的內(nèi)存,從而可能導(dǎo)致OOM。
4. 共享線程池
4.1 示例代碼
/**
* 共享線程池
* <p>
* 優(yōu)勢(shì):當(dāng)在某一時(shí)間段內(nèi)任務(wù)較多,且執(zhí)行較快時(shí)方便使用
* </p>
* <p>
* 風(fēng)險(xiǎn):當(dāng)處理較慢時(shí),會(huì)創(chuàng)建大量的線程
* </p>
*/
@Test
public void newCachedThreadPool() {
ExecutorService executor = Executors.newCachedThreadPool();
// 總共5個(gè)任務(wù)
for (int i = 1; i <= 5; i++) {
int taskIndex = i;
executor.execute(() -> {
log.info("線程 " + Thread.currentThread().getName() + " 正在執(zhí)行任務(wù) " + taskIndex);
// 每個(gè)任務(wù)耗時(shí)1秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
控制臺(tái)打?。?/p>
20:45:31.351 [pool-1-thread-4] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-4 正在執(zhí)行任務(wù) 4
20:45:31.351 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-1 正在執(zhí)行任務(wù) 1
20:45:31.351 [pool-1-thread-5] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-5 正在執(zhí)行任務(wù) 5
20:45:31.358 [pool-1-thread-2] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-2 正在執(zhí)行任務(wù) 2
20:45:31.359 [pool-1-thread-3] INFO com.c3stones.test.ThreadPoolTest - 線程 pool-1-thread-3 正在執(zhí)行任務(wù) 3
每一個(gè)任務(wù)都創(chuàng)建了新的線程。
4.2 源碼剖析
/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
該類型線程池的核心線程數(shù)為0,最大線程數(shù)為Integer.MAX_VALUE(即:2147483647),空閑線程最大存活時(shí)間為60秒,等待隊(duì)列使用SynchronousQueue,該隊(duì)列不存儲(chǔ)數(shù)據(jù),只做轉(zhuǎn)發(fā),具體可參考:【并發(fā)編程】Java 阻塞隊(duì)列。
當(dāng)任務(wù)較多或執(zhí)行較慢時(shí),會(huì)創(chuàng)建大量的線程,從而導(dǎo)致OOM。
5. 定時(shí)線程池
5.1 示例代碼
/**
* 定時(shí)線程池
* <p>
* 優(yōu)點(diǎn):可以定時(shí)執(zhí)行某些任務(wù)
* </p>
* <p>
* 風(fēng)險(xiǎn):當(dāng)處理較慢時(shí),等待隊(duì)列的任務(wù)堆積會(huì)導(dǎo)致OOM
* </p>
*/
@Test
public void newScheduledThreadPool() {
// // 單一線程
// ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// 指定核心線程數(shù)
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
executor.schedule(() -> {
log.info("3秒后開始執(zhí)行,以后不再執(zhí)行");
// 每個(gè)任務(wù)耗時(shí)1秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 3, TimeUnit.SECONDS);
//
// executor.scheduleAtFixedRate(() -> {
// log.info("3秒后開始執(zhí)行,以后每2秒執(zhí)行一次");
//
// // 每個(gè)任務(wù)耗時(shí)1秒
// try {
// TimeUnit.SECONDS.sleep(1);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }, 3, 2, TimeUnit.SECONDS);
//
// executor.scheduleWithFixedDelay(() -> {
// log.info("3秒后開始執(zhí)行,以后延遲2秒執(zhí)行一次");
//
// // 每個(gè)任務(wù)耗時(shí)1秒
// try {
// TimeUnit.SECONDS.sleep(1);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }, 3, 2, TimeUnit.SECONDS);
}
控制臺(tái)打印 - 1:
21:18:46.494 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 3秒后開始執(zhí)行,以后不再執(zhí)行
啟動(dòng)后3秒開始執(zhí)行,執(zhí)行完成后不再繼續(xù)執(zhí)行。
控制臺(tái)打印 - 2:
21:22:47.078 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 3秒后開始執(zhí)行,以后每2秒執(zhí)行一次
21:22:49.075 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 3秒后開始執(zhí)行,以后每2秒執(zhí)行一次
21:22:51.075 [pool-1-thread-2] INFO com.c3stones.test.ThreadPoolTest - 3秒后開始執(zhí)行,以后每2秒執(zhí)行一次
啟動(dòng)后3秒開始執(zhí)行,以后每兩秒執(zhí)行一次。
控制臺(tái)打印 - 3:
21:28:09.701 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 3秒后開始執(zhí)行,以后延遲2秒執(zhí)行一次
21:28:12.705 [pool-1-thread-1] INFO com.c3stones.test.ThreadPoolTest - 3秒后開始執(zhí)行,以后延遲2秒執(zhí)行一次
21:28:15.707 [pool-1-thread-2] INFO com.c3stones.test.ThreadPoolTest - 3秒后開始執(zhí)行,以后延遲2秒執(zhí)行一次
啟動(dòng)后3秒開始執(zhí)行,以后每次執(zhí)行時(shí)間為任務(wù)的耗時(shí)時(shí)間加固定的延遲時(shí)間。
假設(shè)每次任務(wù)固定延遲2秒,第一次任務(wù)在第3秒開始執(zhí)行,任務(wù)耗時(shí)1秒;第二次任務(wù)將在第一次完成后2秒開始執(zhí)行(即第6秒),耗時(shí)2秒;第三次任務(wù)將在第二次完成后2秒開始執(zhí)行(即第10秒),依次類推。
6. SpringBoot中注入異步線程池
6.1 自定義線程配置類
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 自定義線程池配置類
*
* @author CL
*/
@Configuration
public class TaskExecutorConfig {
/**
* 自定義任務(wù)執(zhí)行器
*
* @return {@link TaskExecutor}
*/
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心線程數(shù),默認(rèn)1
int corePoolSize = Runtime.getRuntime().availableProcessors();
executor.setCorePoolSize(corePoolSize);
// 最大線程數(shù),默認(rèn)Integer.MAX_VALUE
executor.setMaxPoolSize(corePoolSize * 2 + 1);
// 空閑線程最大存活時(shí)間,默認(rèn)60秒
executor.setKeepAliveSeconds(3);
// 等待隊(duì)列及大小,默認(rèn)Integer.MAX_VALUE
executor.setQueueCapacity(500);
// 線程的名稱前綴,默認(rèn)該Bean名稱簡寫:org.springframework.util.ClassUtils.getShortName(java.lang.Class<?>)
executor.setThreadNamePrefix("custom-thread-");
// 當(dāng)線程池達(dá)到最大時(shí)的處理策略,默認(rèn)拋出RejectedExecutionHandler異常
// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 拋出RejectedExecutionHandler異常
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 交由調(diào)用者的線程執(zhí)行
// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); // 丟掉最早未處理的任務(wù)
// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); // 丟掉新提交的任務(wù)
// 等待所有任務(wù)結(jié)束后再關(guān)閉線程池,默認(rèn)false
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待所有任務(wù)結(jié)束最長等待時(shí)間,默認(rèn)0毫秒
executor.setAwaitTerminationSeconds(10);
// 執(zhí)行初始化
executor.initialize();
return executor;
}
}
- 在Service注入使用
/**
* 示例Service
*
* @author CL
*/
public interface DemoService {
/**
* 示例方法
*
* @return {@link String}
*/
void demo();
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 示例Service實(shí)現(xiàn)
*
* @author CL
*/
@Slf4j
@Service
public class DemoServiceImpl implements DemoService {
@Resource
private TaskExecutor taskExecutor;
/**
* 示例方法
*/
@Override
public void demo() {
taskExecutor.execute(() -> {
log.info("線程 " + Thread.currentThread().getName() + " 正在執(zhí)行Service中的方法");
});
}
}
- 異步任務(wù)指定線程池
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
/**
* 示例異步任務(wù)
*
* @author CL
*/
@Slf4j
@Component
@EnableAsync
public class DemoAsync {
/**
* 示例方法
*/
@Async(value = "taskExecutor")
public void demo() {
log.info("線程 " + Thread.currentThread().getName() + " 正在執(zhí)行Async中的方法");
}
}
- 定時(shí)任務(wù)調(diào)度指定線程池
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 自定義定時(shí)任務(wù)調(diào)度配置類
*
* @author CL
*/
@Configuration
public class SheduledConfig implements SchedulingConfigurer {
/**
* 配置定時(shí)任務(wù)
*
* @param scheduledTaskRegistrar 配置任務(wù)注冊(cè)器
*/
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.setScheduler(taskScheduler());
// // 第二種方式
// scheduledTaskRegistrar.setScheduler(scheduledExecutorService());
}
/**
* 自定義任務(wù)調(diào)度器
*
* @return {@link TaskScheduler}
*/
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
executor.setPoolSize(5);
executor.setThreadNamePrefix("custom-scheduler-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
// /**
// * 自定義任務(wù)線程池
// *
// * @return {@link ScheduledExecutorService}
// */
// @Bean
// public ScheduledExecutorService scheduledExecutorService() {
// return Executors.newScheduledThreadPool(5);
// }
}
6.2 測(cè)試
- 編寫測(cè)試Controller
import com.c3tones.async.DemoAsync;
import com.c3tones.service.DemoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 示例Controller
*
* @author CL
*/
@Slf4j
@RestController
public class DemoController {
@Resource
private DemoService demoService;
@Resource
private DemoAsync demoAsync;
/**
* Service示例方法
*
* @return {@link String}
*/
@RequestMapping("/service")
public void service() {
log.info("Service示例方法開始執(zhí)行");
demoService.demo();
log.info("Service示例方法結(jié)束執(zhí)行");
}
/**
* 異步示例方法
*
* @return {@link String}
*/
@RequestMapping("/async")
public void async() {
log.info("異步示例方法開始執(zhí)行");
demoAsync.demo();
log.info("異步示例方法結(jié)束執(zhí)行");
}
}
- 啟動(dòng)項(xiàng)目
- 測(cè)試Service中的自定義線程池
curl http://127.0.0.1:8080/service
控制臺(tái)打印:
2023-03-19 22:26:26.896 INFO 136568 --- [nio-8080-exec-3] com.c3tones.controller.DemoController : Service示例方法開始執(zhí)行
2023-03-19 22:26:26.897 INFO 136568 --- [nio-8080-exec-3] com.c3tones.controller.DemoController : Service示例方法結(jié)束執(zhí)行
2023-03-19 22:26:26.897 INFO 136568 --- [custom-thread-1] com.c3tones.service.DemoServiceImpl : 線程 custom-thread-1 正在執(zhí)行Service中的方法
調(diào)用接口同步打印日志,自定義線程異步執(zhí)行任務(wù)。
- 測(cè)試異步任務(wù)中的自定義線程池
curl http://127.0.0.1:8080/async
控制臺(tái)打?。?/p>
2023-03-19 22:28:08.349 INFO 136568 --- [nio-8080-exec-7] com.c3tones.controller.DemoController : 異步示例方法開始執(zhí)行
2023-03-19 22:28:08.355 INFO 136568 --- [nio-8080-exec-7] com.c3tones.controller.DemoController : 異步示例方法結(jié)束執(zhí)行
2023-03-19 22:28:08.363 INFO 136568 --- [custom-thread-2] com.c3tones.async.DemoAsync : 線程 custom-thread-2 正在執(zhí)行Async中的方法
調(diào)用接口同步打印日志,異步線程異步執(zhí)行任務(wù)。
測(cè)試定時(shí)任務(wù)中的自定義線程池
編寫測(cè)試方法
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 示例定時(shí)任務(wù)
*
* @author CL
*/
@Slf4j
@Component
@EnableScheduling
public class DemoScheduled {
/**
* 示例方法
*/
@Scheduled(cron = "0/3 * * * * ? ")
public void demo() {
log.info("線程 " + Thread.currentThread().getName() + " 正在執(zhí)行Scheduled中的方法");
}
}
啟動(dòng)服務(wù)
控制臺(tái)打?。?/p>
2023-03-19 22:30:24.002 INFO 136568 --- [tom-scheduler-3] com.c3tones.sheduled.DemoScheduled : 線程 custom-scheduler-3 正在執(zhí)行Scheduled中的方法
2023-03-19 22:30:27.002 INFO 136568 --- [tom-scheduler-3] com.c3tones.sheduled.DemoScheduled : 線程 custom-scheduler-3 正在執(zhí)行Scheduled中的方法
2023-03-19 22:30:30.001 INFO 136568 --- [tom-scheduler-3] com.c3tones.sheduled.DemoScheduled : 線程 custom-scheduler-3 正在執(zhí)行Scheduled中的方法
定時(shí)任務(wù)從0秒開始,每3秒執(zhí)行一次任務(wù)。
7. 項(xiàng)目地址
到此這篇關(guān)于SpringBoot創(chuàng)建線程池的六種方式小結(jié)的文章就介紹到這了,更多相關(guān)SpringBoot創(chuàng)建線程池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決scala.collection.mutable.Map寫入的問題
這篇文章主要介紹了解決scala.collection.mutable.Map寫入的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Spring @Primary作用和實(shí)現(xiàn)原理詳解
今天分享一下Spring中的@Primary注解,Primary的意思是主要的,我們?cè)谑褂胹pring的時(shí)候,難免會(huì)定義多個(gè)類型相同的bean,這時(shí)候如果不采取一些方法,那么是無法正常使用bean的,所以本就給大家介紹Spring @Primary的作用和實(shí)現(xiàn)原理2023-07-07
Java輕松實(shí)現(xiàn)批量插入或刪除Excel行列操作
在職場(chǎng)生活中,對(duì)Excel工作表的行和列進(jìn)行操作是非常普遍的需求,下面小編就來和大家介紹一下如何在Java中完成批量插入、刪除行和列的操作吧2023-10-10
Java農(nóng)夫過河問題的繼承與多態(tài)實(shí)現(xiàn)詳解
這篇文章主要介紹了Java農(nóng)夫過河問題的繼承與多態(tài)實(shí)現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
Security中的@PostAuthorize、@PreFilter和@PostFilter詳解
這篇文章主要介紹了Security中的@PostAuthorize、@PreFilter和@PostFilter詳解,@PostAuthorize是在方法調(diào)用完成后進(jìn)行權(quán)限檢查,它不能控制方法是否能被調(diào)用,只能在方法調(diào)用完成后檢查權(quán)限決定是否要拋出AccessDeniedException,需要的朋友可以參考下2023-11-11
Spring Boot容器加載時(shí)執(zhí)行特定操作(推薦)
這篇文章主要介紹了Spring Boot容器加載時(shí)執(zhí)行特定操作及spring內(nèi)置的事件,需要的朋友可以參考下2018-01-01
Gradle修改本地倉庫的位置方法實(shí)現(xiàn)
這篇文章主要介紹了Gradle修改本地倉庫的位置方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
基于SpringBoot實(shí)現(xiàn)Web應(yīng)用的登錄與退出功能
登錄與退出功能作為 Web 應(yīng)用中的基礎(chǔ)且重要的組成部分,直接關(guān)系到用戶的安全和隱私保護(hù),所以本文給大家介紹了基于SpringBoot實(shí)現(xiàn)Web應(yīng)用的登錄與退出功能,文中有詳細(xì)的代碼供大家參考,需要的朋友可以參考下2024-04-04

