文章目录
- 1. 线程池简介
- 2. 创建线程池
- 3. 工厂模式简介
- 4. 线程池的使用
- 5. 实现线程池
- 6. ThreadPoolExecutor的构造方法讲解
- 7. 线程池的线程数量,如何确定?
1. 线程池简介
Java线程池是一种用于管理和重用线程的机制,它可以在需要执行任务时,从线程池中获取线程,执行任务,然后将线程放回池中,以便后续使用。线程池可以有效地管理线程的数量,提高程序的性能和资源利用率。
为什么从线程池里面取线程比直接创建线程快呢?
创建线程是要在操作系统内核中完成的,涉及"用户态"到"内核态"切换操作. 这个切换是有一定开销的. 而线程池取线程是纯的用户态实现.
2. 创建线程池
注意这里线程池创建的写法:
ExecutorService executorService = Executors.newCachedThreadPool();
这里并没有new,而是通过Executors
的静态方法newCachedThreadPool
来创建的,这里涉及到一种设计模式-“工厂模式”
3. 工厂模式简介
工厂模式(Factory Pattern)是一种创建对象的设计模式,它提供了一种封装对象创建过程的方式,使得客户端代码与具体对象的实例化过程解耦。
工厂模式的主要目的是通过一个工厂类来创建对象,而不是直接在客户端代码中使用
new
关键字来实例化对象。这样可以隐藏具体对象的创建细节,提供更灵活、可扩展的设计。
详细可参考:工厂模式 | 菜鸟教程
4. 线程池的使用
线程池的使用很简单,只需要实现submit
方法即可
public class Demo19 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("线程执行的任务1");
}
});
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("线程执行的任务2");
}
});
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("线程执行的任务3");
}
});
}
}
这里主要是将任务放到线程池中,线程池里的线程就会去执行这些任务
运行结果:
5. 实现线程池
要想实现线程池,要先弄清楚线程池有哪些功能.
一个线程池中可以同时提交N个任务,线程池里有M个线程来执行这N个任务
如何将N个任务分配给M个线程呢?
可以使用生产者-消费者模型解决这个问题.
以下是一个线程池的简易实现:
public class MyThreadPool {
private BlockingQueue<Runnable> blockingQueue = new public class MyThreadPool {
private BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>();
public void submit(Runnable runnable) throws InterruptedException {
// 将任务添加到阻塞队列中
blockingQueue.put(runnable);
}
public MyThreadPool(int m){
// 创建m个线程
for (int i = 0; i < m; i++) {
Thread t = new Thread(() ->{
// 让它一直扫描阻塞队列
while (true) {
try {
// 取出任务,并执行
Runnable runnable = blockingQueue.take();
runnable.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
}
}
测试上述代码:
public class Demo20 {
public static void main(String[] args) throws InterruptedException {
MyThreadPool pool = new MyThreadPool(5);
for (int i = 0; i < 10; i++) {
int temp = i;
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("任务"+ temp);
}
});
}
}
}
运行结果:
不过上述代码中创建的线程数量是"固定"的,可根据需要自行调整
6. ThreadPoolExecutor的构造方法讲解
ThreadPoolExecutor
是Java中线程池的一个具体实现类,它实现
了ExecutorService接口。ExecutorService是一个高级的线程池管理接口,提供了更多的灵活性和功能。
而ThreadPoolExecutor
这个类的构造方法比较复杂
参数:
-
corePoolSize
- 即使空闲时仍保留在池中的线程数,除非设置allowCoreThreadTimeOut
-
maximumPoolSize
- 池中允许的最大线程数 -
keepAliveTime
- 当线程数大于内核时,这是多余的空闲线程在终止前等待新任务
的最大时间。 -
unit
-keepAliveTime
参数的时间单位 -
workQueue
- 用于在执行任务之前使用的队列。 这个队列将仅保存execute
方法提交的Runnable
任务。(自己传入的任务队列,如果传了就用传入的队列.如果不传,就线程池就去创建) -
threadFactory
- 执行程序创建新线程时使用的工厂 -
handler
-任务执行被拒绝时使用的处理程序,因为达到线程限制和队列容量
任务执行被拒绝时使用的程序有以下四个:
实际开发中,主要根据需求选择这里的拒绝策略进行处理.
7. 线程池的线程数量,如何确定?
线程池可以自定义线程的数量
那么在实际开发中,线程池的线程数量设置成多少比较合适?
这个问题主要取决于以下几点:
- 任务类型:不同类型的任务对线程数量的需求不同。如果是CPU密集型任务,通常线程数量应该与CPU核心数相近,以充分利用CPU资源。如果是IO密集型任务,可以设置较多的线程数量,以便处理并发的IO操作。
- 系统资源:需要考虑系统的硬件资源和性能。如果系统资源有限,例如内存、CPU等,线程数量应适当控制,避免过多的线程导致资源竞争和性能下降。
- 并发量:需要根据任务的并发量来确定线程数量。如果任务并发量较高,可以增加线程数量以提高任务处理能力。如果任务并发量较低,可以减少线程数量以节省资源。
- 响应时间:需要考虑任务的响应时间要求。如果任务需要快速响应,可以增加线程数量以减少等待时间。如果任务的响应时间可以接受较长的延迟,可以减少线程数量以节省资源。
- 线程池类型:不同类型的线程池有不同的线程数量限制。例如,FixedThreadPool固定线程池的线程数量是固定的,CachedThreadPool可缓存线程池的线程数量是动态调整的。
这个问题要考虑的因素有很多,最简单的方法就是去测试
将线程分为设置成不同的数量,然后观察系统的性能和任务的执行情况,根据需要逐步调整线程数量,找到最优的配置。
感谢你的观看!希望这篇文章能帮到你!
专栏: 《从零开始的Java学习之旅》在不断更新中,欢迎订阅!
“愿与君共勉,携手共进!”