一、线程池主要核心原理
- 创建一个池子,池子中是空的
- 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
- 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
二、线程池代码实现
Executors:线程池的工具类,通过调用方法返回不同类型的线程池对象
- public static ExecutorService newCachedThreadPool(); 创建一个没有上限的线程池
- public static ExecutorService newFixedThreadPool(int nThreads); 创建有上限的线程池
具体实现步骤
1、创建线程池
2、提交任务
3、所有的任务全部执行完毕,关闭线程池
执行任务MyRunnable类
package com.hidata.devops.paas.demo; /** * @Description : * @Date: 2023-10-11 09:41 */ public class MyRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() ); } }
测试类
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @Description : * @Date: 2023-10-11 09:42 */ public class TestDemo { public static void main(String[] args) { //创建线程池对象 ExecutorService executorService = Executors.newFixedThreadPool(3); //提交任务 executorService.submit(new MyRunnable()); executorService.submit(new MyRunnable()); executorService.submit(new MyRunnable()); executorService.submit(new MyRunnable()); executorService.submit(new MyRunnable()); executorService.submit(new MyRunnable()); executorService.submit(new MyRunnable()); executorService.submit(new MyRunnable()); //关闭线程池(一般不会关闭) executorService.shutdown(); } }
运行结果
pool-1-thread-1 pool-1-thread-3 pool-1-thread-2 pool-1-thread-1 pool-1-thread-2 pool-1-thread-3 pool-1-thread-2 pool-1-thread-1 Process finished with exit code 0
总结
- 上述代码中,我们创建的线程池,设置的最大线程数为3,当连续提交了8个任务,可以看到打印的结果,只有三个线程在轮换着执行任务,所以当任务数超过最大线程数时,线程池不会创建新的线程,而是等线程池中有空闲的线程时,会复用该空闲线程,继续执行任务,再次强调一遍:不会创建新线程,因为最大线程数为3
- 实际开发中,线程池一般不会关闭哦
三、自定义线程池
通过Executors工具类调用方法,创建线程池,不够灵活,因为很多的线程池参数都没有办法自定义。所以我们需要自定义线程池。
1、自定义线程池原理
1、创建一个空的池子
2、有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程
不断地提交任务,会有以下三个临界点:
1、当核心线程满时,再提交任务就会排队
2、当核心线程满,队伍满时,会创建临时线程
3、当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略
2、自定义线程池参数
3、自定义线程池任务拒绝策略
3、代码实现
package com.hidata.devops.paas.demo; /** * @Description : * @Date: 2023-10-11 09:41 */ public class MyRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() ); } }
package com.hidata.devops.paas.demo; import java.util.concurrent.*; /** * @Description : * @Date: 2023-10-11 09:42 */ public class TestDemo { public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor( 3,//核心线程数量,不能小于0 6,//最大线程数,不能小于0,最大数量>=核心线程数量 60,//空闲线程最大存活时间 TimeUnit.SECONDS,//时间单位 new ArrayBlockingQueue<>(3),//任务队列 Executors.defaultThreadFactory(),//创建线程工厂 new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略 ); executor.submit(new MyRunnable()); executor.submit(new MyRunnable()); executor.submit(new MyRunnable()); executor.submit(new MyRunnable()); executor.submit(new MyRunnable()); executor.submit(new MyRunnable()); executor.submit(new MyRunnable()); executor.submit(new MyRunnable()); executor.shutdown(); } }
运行结果
pool-1-thread-2 pool-1-thread-4 pool-1-thread-3 pool-1-thread-3 pool-1-thread-1 pool-1-thread-4 pool-1-thread-2 pool-1-thread-5 Process finished with exit code 0
四、线程池多大合适呢?
线程池大小不能随便写的,要根据你项目的类型,一般分为两种类型的项目:
- CPU密集型项目:一般是你项目中的计算比较多,但是读取数据库或者读取本地文件操作比较少
- I/O密集型项目:读取数据库操作或者读取文件操作比较多
针对这两种类型的项目,是有一个公式的,先看一下这个复杂的公式:
我们发现公式里面出现了多个关键字:最大并行数,那怎么获取操作系统的最大并行数呢?
java底层虚拟机已经提供了获取操作系统最大并行数的方法了:
int count = Runtime.getRuntime().availableProcessors(); System.out.println(count);
也可以打开你电脑的任务管理器,其中逻辑处理器就是最大并行数