Executors
工具类调用对应的静态方法进行创建// 固定大小线程池, 其中 n 为线程池中的线程数
// [注意] corePoolSize 和 maxPoolSize 都设置成了指定的线程数量,即 corePoolSize == maxPoolSize
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(n);
// 缓存线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 单线程线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 定时任务线程池,其中 n 为要保留在池中的线程数,即核心线程数
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(n);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}
1、【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯
2、【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
3、【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
我们也可以通过自定义线程池来创建符合我们需求的线程池。自定义线程池需要设置以下 7 大核心参数:
下面是一个简单的自定义线程池 demo,模拟任务执行:
import java.util.concurrent.*;
/**
* @Author Jasper
* @Time 2021/02/01
*/
public class ThreadPoolDemo {
public final static ThreadPoolExecutor CUSTOMER_EXECUTOR = new ThreadPoolExecutor(
3,
5,
10,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
public static void main(String[] args) {
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
CUSTOMER_EXECUTOR.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
CUSTOMER_EXECUTOR.shutdown();
}
}
/**
* 输出:
* pool-1-thread-1 执行任务 0
* pool-1-thread-2 执行任务 1
* pool-1-thread-3 执行任务 2
* pool-1-thread-3 执行任务 3
* pool-1-thread-1 执行任务 4
* pool-1-thread-2 执行任务 5
* pool-1-thread-2 执行任务 6
* pool-1-thread-3 执行任务 8
* pool-1-thread-1 执行任务 7
* pool-1-thread-2 执行任务 9
*/
另一种创建自定义线程池的方式:SpringBoot 配置文件注入 Bean 对象
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Author Jasper
* @Time 2024/02/01
*/
@Configuration
@EnableAsync
public class CustomerExecutorConfig {
/**
* 核心线程数(默认线程数)
*/
private static final int CORE_POOL_SIZE = 5;
/**
* 最大线程数
*/
private static final int MAX_POOL_SIZE = 20;
/**
* 允许线程空闲时间(单位:默认为秒)
*/
private static final int KEEP_ALIVE_TIME = 10;
/**
* 缓冲队列大小
*/
private static final int QUEUE_CAPACITY = 200;
/**
* 线程池名前缀
*/
private static final String THREAD_NAME_PREFIX = "Jasper-Async-";
@Bean("customerExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
// 设置相关参数
threadPoolTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
threadPoolTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
threadPoolTaskExecutor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
threadPoolTaskExecutor.setQueueCapacity(QUEUE_CAPACITY);
threadPoolTaskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
// 初始化
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
编写模拟任务的代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
/**
* @Author Jasper
* @Time 2024/02/01
*/
@Component
@Slf4j
public class AsyncTask {
@Async("customerExecutor")
public void doTask() {
log.info("开始做任务...");
long start = System.currentTimeMillis();
try {
// 模拟任务耗时
Thread.sleep(2000);
} catch (InterruptedException e) {
log.error("异常:{}", e.getMessage(), e);
}
long end = System.currentTimeMillis();
log.info("完成任务,耗时:" + (end - start) + "毫秒");
}
public String priorityTask() {
Thread.currentThread().setPriority(1);
return "高优先级任务,id:" + Thread.currentThread().getId() + " 由 " + Thread.currentThread().getName() + " 执行";
}
}
Controller 层:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @Author Jasper
* @Time 2024/02/01
*/
@RestController
public class AsyncController {
@Resource
private AsyncTask asyncTask;
@GetMapping("/myAsyncTask")
public void test() {
asyncTask.doTask();
System.out.println(asyncTask.priorityTask());
}
}
启动服务,访问测试:localhost:9999/myAsyncTask
输出:
高优先级任务,id:34 由 http-nio-9999-exec-1 执行
2024-01-31 09:55:40.794 INFO 17676 --- [ Jasper-Async-1] c.example.demo.threadCoustom.AsyncTask : 开始做任务...
2024-01-31 09:55:42.794 INFO 17676 --- [ Jasper-Async-1] c.example.demo.threadCoustom.AsyncTask : 完成任务,耗时:2000毫秒
可以看到,使用了指定的自定义线程池的任务,就会由自定义的线程池去执行任务,而没有指定的就由默认线程去执行任务
总结:线程池是 Java 中重要的线程管理机制,通过线程池可以对线程进行有效的管理和控制,提高程序的性能和可靠性。Java 中内置了多种类型的线程池,同时也支持自定义线程池,通过设置不同的参数,可以创建符合需求的线程池。使用线程池可以减少线程创建和销毁的开销,提高程序的性能和可靠性,同时也需要注意线程池的配置和使用方式,以充分发挥其优势。
以上就是线程池的相关内容了,小伙伴们在真实的场景中是怎么使用线程池的呢?可以在评论区讨论哦~
创作不易,感谢阅读,若遇到问题,可以关注此微信gzh:EzCoding 留言反馈,希望能够帮助到您