线程池是一种治理和复用线程资源的机制,它由一个线程池管理器和一组工作线程组成。线程池管理器负责创立和销毁线程池,以及治理线程池中的工作线程。工作线程则负责执行具体的工作。
线程池的次要作用是治理和复用线程资源,防止了线程的频繁创立和销毁所带来的开销。
线程池蕴含两个重要的组成部分:
- 线程池大小:指线程池中所能包容的最大线程数。线程池大小个别依据零碎的负载状况和硬件资源来设置。
- 工作队列:用于寄存期待执行的工作。当线程池中的工作线程曾经全副被占用时,新的工作将被退出到工作队列中期待执行。
线程池创立形式
Java 中线程池的创立形式次要有以下几种:
- 应用 ThreadPoolExecutor 类手动创立:通过 ThreadPoolExecutor 类的构造函数自定义线程池的参数,包含外围线程数、最大线程数、线程存活工夫、工作队列等。
- 应用 Executors 类提供的工厂办法创立:通过 Executors 类提供的一些动态工厂办法创立线程池,例如 newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool 等。
- 应用 Spring 框架提供的 ThreadPoolTaskExecutor 类:在 Spring 框架中能够通过 ThreadPoolTaskExecutor 类来创立线程池。
不同的创立形式实用于不同的场景,通常能够依据理论状况抉择适合的形式创立线程池。手动创立 ThreadPoolExecutor 类能够灵便地配置线程池参数,但须要对线程池的各项参数有肯定的理解;应用 Executors 工厂办法能够疾速创立线程池,但可能无奈满足特定的需要,且容易呈现内存溢出的状况;而 Spring 框架提供的 ThreadPoolTaskExecutor 类则只能在 Spring 框架中应用。
线程池应用
ThreadPoolExecutor 应用
ThreadPoolExecutor 线程的创立与应用示例如下:
import java.util.concurrent.*;
public class ThreadPoolDemo {public static void main(String[] args) {
// 创立线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 外围线程数
5, // 最大线程数
60, // 线程池中线程的闲暇工夫(单位为秒)TimeUnit.SECONDS, // 工夫单位
new ArrayBlockingQueue<>(10) // 工作队列
);
// 提交工作
for (int i = 1; i <= 20; i++) {
final int taskId = i;
executor.submit(() -> {System.out.println("执行工作:" + taskId + ",线程名称:" + Thread.currentThread().getName());
try {Thread.sleep(1000); // 模仿工作执行工夫
} catch (InterruptedException e) {e.printStackTrace();
}
});
}
// 敞开线程池
executor.shutdown();}
}
在下面的示例中,咱们通过 ThreadPoolExecutor 类手动创立了一个线程池,设置了线程池的外围线程数为 2,最大线程数为 5,线程闲暇工夫为 60 秒,工作队列为长度为 10 的 ArrayBlockingQueue。而后咱们通过 submit() 办法向线程池中提交了 20 个工作,每个工作会在执行时输入本人的编号和执行线程的名称,而后睡眠 1 秒钟模仿工作执行工夫。最初,咱们调用 shutdown() 办法敞开线程池。
Executors 应用
import java.util.concurrent.*;
public class ExecutorsDemo {public static void main(String[] args) {
// 创立线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交工作
for (int i = 1; i <= 10; i++) {
final int taskId = i;
executor.submit(() -> {System.out.println("执行工作:" + taskId + ",线程名称:" + Thread.currentThread().getName());
try {Thread.sleep(1000); // 模仿工作执行工夫
} catch (InterruptedException e) {e.printStackTrace();
}
});
}
// 敞开线程池
executor.shutdown();}
}
在下面的示例中,咱们应用了 Executors 工厂类中的 newFixedThreadPool() 办法创立了一个线程池,该线程池有 5 个固定线程。而后咱们通过 submit() 办法向线程池中提交了 10 个工作,每个工作会在执行时输入本人的编号和执行线程的名称,而后睡眠 1 秒钟模仿工作执行工夫。最初,咱们调用 shutdown() 办法敞开线程池。值得注意的是,应用 Executors 工厂类创立线程池尽管非常简单,然而在理论生产环境中并不举荐,因为这种形式很容易导致线程资源被耗尽,从而影响零碎的性能和稳定性。
ThreadPoolTaskExecutor 应用
Spring 中 ThreadPoolTaskExecutor 的应用示例如下:
import java.util.concurrent.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
public class ThreadPoolTaskExecutorDemo {public static void main(String[] args) {
// 创立线程池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2); // 外围线程数
executor.setMaxPoolSize(5); // 最大线程数
executor.setQueueCapacity(10); // 工作队列长度
executor.initialize();
// 提交工作
for (int i = 1; i <= 20; i++) {
final int taskId = i;
executor.submit(() -> {System.out.println("执行工作:" + taskId + ",线程名称:" + Thread.currentThread().getName());
try {Thread.sleep(1000); // 模仿工作执行工夫
} catch (InterruptedException e) {e.printStackTrace();
}
});
}
// 敞开线程池
executor.shutdown();}
}
在下面的示例中,咱们应用了 Spring 框架中的 ThreadPoolTaskExecutor 类创立了一个线程池,设置了线程池的外围线程数为 2,最大线程数为 5,工作队列长度为 10。而后咱们通过 submit() 办法向线程池中提交了 20 个工作,每个工作会在执行时输入本人的编号和执行线程的名称,而后睡眠 1 秒钟模仿工作执行工夫。最初,咱们调用 shutdown() 办法敞开线程池。留神,在应用 Spring 框架中的 ThreadPoolTaskExecutor 类创立线程池时,咱们须要先调用 initialize() 办法进行初始化。
ThreadPoolTaskExecutor 底层还是通过 JDK 中提供的 ThreadPoolExecutor 类实现的。
线程池长处详解
线程池相比于线程来说,它不须要频繁的创立和销毁线程,线程一旦创立之后,默认状况下就会始终放弃在线程池中,等到有工作来了,再用这些已有的线程来执行工作,如下图所示:
长处 1:复用线程,升高资源耗费
线程在创立时要开拓虚拟机栈、本地办法栈、程序计数器等公有线程的内存空间,而销毁时又要回收这些公有空间资源,如下图所示:
而线程池创立了线程之后就会放在线程池中,因而线程池相比于线程来说,第一个长处就是 能够复用线程、减低系统资源的耗费。
长处 2:进步响应速度
线程池是复用已有线程来执行工作的,而线程是在有工作时才新建的,所以相比于线程来说,线程池可能更快的响应工作和执行工作。
长处 3:管控线程数和工作数
线程池提供了更多的治理性能,这里治理性能次要体现在以下两个方面:
- 管制最大并发数:线程池能够创立固定的线程数,从而防止了有限创立线程的问题。当线程创立过多时,会导致系统执行变慢,因为 CPU 核数是肯定的、能同时解决的工作数也是肯定的,而线程过多时就会造成线程歹意争抢和线程频繁切换的问题,从而导致程序执行变慢,所以适合的线程数才是高性能运行的要害。
- 管制工作最大数:如果工作有限多,而内存又有余的状况下,就会导致程序执行报错,而线程池能够管制最大工作数,当工作超过肯定数量之后,就会采纳回绝策略来解决多出的工作,从而保障了零碎能够衰弱的运行。
长处 4:更多加强性能
线程池相比于线程来说提供了更多的性能,比方定时执行和周期执行等性能。
小结
线程池是一种治理和复用线程资源的机制。相比于线程,它具备四个次要劣势:1. 复用线程,升高了资源耗费;2. 进步响应速度;3. 提供了治理线程数和工作数的能力;4. 更多加强性能。
本文已收录至《Java 面试突击》,专一 Java 面试 100 年,查看更多:www.javacn.site