网站首页 > 文章精选 正文
线程池化技术,是通过一个公用的线程队列,将一个或多个线程进行统一资源调用,可以支持线程资源的重复使用的技术,可以有效的避免因为线程调用创建和销毁线程过程带来的资源消耗问题。
为什么要使用线程池?
1、可以通过重复利用创建好的线程来降低线程创建和销毁造成的资源消耗
2、可以提升系统响应速度,因为核心线程是一直存在的,所以多线程任务不需要等待线程创建就可以立即执行任务。
3、可以利用线程池对线程资源进行统一的管理,避免线程创建过多导致的内存溢出问题等问题。
Java中如何来实现线程池的管理的?
Java是从JDK1.5开始的时候,将工作单元与线程执行机制进行了分离来提供线程池的使用管理机制,其中工作单元主要就是包括Runnable和Callable,而线程的执行机制则是交给了Executors来提供,代码如下。
首先我们来创建一个用于测试的线程类。
public class TestThread implements Runnable {
private String count;
public TestThread(String count) {
this.count = count;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 开始计数 Count = "+count);
executeCount();
System.out.println(Thread.currentThread().getName()+" 结束计数");
}
private void executeCount() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString(){
return this.count;
}
}
创建测试主类
public class SimpleTheadPool {
public static void main(String[] args) {
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//创建执行线程操作
for (int i = 0; i < 10; i++) {
Runnable testThread = new TestThread(String.valueOf(i));
executorService.execute(testThread);
}
executorService.shutdown();
while (executorService.isTerminated()){
}
System.out.println("完成所有线程");
}
}
在测试类中,创建了一个固定大小的线程池,并且给线程池分配了10个需要执行的测试任务,因为线程池的初始大小是5,所以无法一次容纳10个线程的处理,所以当线程进入之后,会有一部分线程进入到等待状态,当前面进入的线程执行完成之后,后续的线程就会自动进入执行,执行效果如下图所示。
从执行结果来看,线程池中只存在了五个线程,并且这五个线程并不会随着工作执行完成而销毁,会一直等待分配线程执行任务,一直到线程池调用了销毁函数来进行销毁。
Executors使用了ExecutorService提供的线程实现,并且newFixedThreadPool() 方法创建了如下的一个线程池类。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
下面我们就来看看ThreadPoolExecutor线程池类
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// 省略代码
}
corePoolSize:核心线程数,当我们往线程池中添加一个任务之后,线程池会创建一个新的线程去执行任务,当创建的线程数等于corePoolSize之后。继续提交任务就会被阻塞到队列中,等待执行。当然如果调用了prestartAllCoreThreads()方法,线程池也会启动所有的核心线程。
workQueue:用来保存等待执行任务的阻塞队列。
maximumPoolSize:线程池中被允许的最大线程数。当等待执行阻塞队列装满之后,如果继续提交任务,则会继续创建线程去执行任务,前提是创建的线程数要小于maximumPoolSize值。这里需要注意的是如果队列可以存放的任务是无限的,那么这个参数就不会起作用,因为队列会一直存放等待线程。
keepAliveTime:线程空闲等待时间,当线程没有需要执行的任务的时候,就会在等待指定时间之后被销毁,默认情况下只会销毁超过corePoolSize数的线程。
unit:设置等待的线程时间单位
threadFactory: 创建线程的工厂类。通过自定义的方式可以给每个线程创建一个具有识别性的线程名称,用于后续问题定位。
handler: 线程拒绝策略,当队列满的时候,如果没有空闲线程执行任务,那么就需要拒绝策略的参与。
实现方式
newFixedThreadPool
当线程数达到了核心线程数之后,即使没有可以执行的任务,线程池也不会释放线程。并且这个线程池采用了一个无界的队列,也就是说阻塞队列永远不会达到饱和。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newSingleThreadExecutor
初始化线程池中只有一个线程执行任务,如果这个线程因为异常而结束工作,则会重新创建一个新的线程来继续执行该任务,这个唯一线程的执行操作就可以保证所有的提交任务都是按照队列排序顺序执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newCachedThreadPool
这线程池的线程数可以达到Integer.MAX_VALUE值,并且内部使用了SynchronousQueue队列来作为阻塞队列。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
总结
上面我们介绍了在Java中如何去创建一个线程池,并且介绍了创建线程池相关的核心参数。在后续的分享中还会给大家详细介绍关于Java中线程池的使用,敬请期待
- 上一篇: 一篇详解内核监控进程与线程创建
- 下一篇: 探讨C语言系统编程中线程的原理以及实现
猜你喜欢
- 2025-01-02 Socket与TCP协议,利用python打造一个多人聊天室
- 2025-01-02 探讨C语言系统编程中线程的原理以及实现
- 2025-01-02 ffmpeg播放器实现详解 - 创建线程
- 2025-01-02 一篇详解内核监控进程与线程创建
- 2025-01-02 Python多线程,线程与进程的区别,线程模块及线程的两种创建方式
- 2025-01-02 正确使用线程池的姿势,你在工作中不要只会使用默认的方式创建
- 2025-01-02 Java线程池的正确创建方式
- 2025-01-02 [编程基础] C++多线程入门1-创建线程的三种不同方式
- 2025-01-02 Qt多线程创建
- 2025-01-02 腾讯二面:Linux操作系统里一个进程最多可以创建多少个线程?
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 稳压管的稳压区是工作在什么区 (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)