当前位置: 首页 > news >正文

Java多线程—线程池

一、引入

采用非线程池方式来开启新线程,例如:继承Thread类,实现Runnable接口,实现Callable接口,这三种方式都是有一个任务,就创建一个线程来执行,任务执行完之后,就销毁该线程,下一次还有任务,就再创建一个线程来执行;时常创建、销毁线程带来的资源浪费十分不合理。

举一个简单的例子:比如我们在使用一次性筷子,一双筷子用完就扔了,下一次再要用筷子的时候就再买一双一次性筷子,这就好比原来的非线程池方式来创建线程,“一次性”这个词很适合形容这种方式。如果我们不使用一次性筷子,我们家里的多次使用的筷子,都放在筷笼里,每次吃饭时就从里面拿出来用,吃完饭,把筷子洗一洗就放进筷笼里,长期使用。

线程池就类似这个筷笼,程序员创建一个线程池(ThreadPool),每当提交任务的时候,线程池就会创建一个线程来执行任务,当任务执行完之后,该线程又会回到线程池里,等待下一次分配。

二、线程池主要核心原理

                                                                 1-线程池工作结构图

核心流程:

  1. 程序员创建一个线程池,这个线程池是空的
  2. 提交一个任务,线程池就创建一个线程来执行该任务,任务执行完之后,线程放回线程池中,等待下一次复用
  3. 如果线程池里的线程都在使用中,有新的任务提交,那么就要在任务队列里等待空闲线程

三、快速上手

我们使用Excutors工具类来调用线程池创建方法,然后创建线程。我们一般常用的线程池有以下两种:

  • 无上限线程池:newCacheThreadPool
  • 有上限线程池:newFixedThreadPool

1、newCacheThreadPool

public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("线程"+Thread.currentThread().getName()
+"正在执行"+"--"+i);}}
}
public class Test {public static void main(String[] args) throws InterruptedException {//创建一个没有上限的线程池(最大线程数量是int类型的上限)ExecutorService executorService = Executors.newCachedThreadPool();//提交任务executorService.submit(new MyRunnable());executorService.submit(new MyRunnable());executorService.submit(new MyRunnable());}
}

创建一个无上限的线程池,并提交了三个任务,每个任务循环打印一百次内容,我们可以看到下面的输出:



因为我们同时提交了三个任务,可以看出来有三个线程在执行任务

我们接下来稍微改一下代码,看看线程池里的线程能不能复用,我们让main线程在每次任务提交之后睡眠1秒,确保每次任务都能在下一次任务提交前执行完

public class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("线程"+Thread.currentThread().getName()+"正在执行");}
}
public class Test {public static void main(String[] args) throws InterruptedException {//创建一个没有上限的线程池(最大线程数量是int类型的上限)ExecutorService executorService = Executors.newCachedThreadPool();//提交任务executorService.submit(new MyRunnable());Thread.sleep(1000);executorService.submit(new MyRunnable());Thread.sleep(1000);executorService.submit(new MyRunnable());}
}

我们可以看到,线程池里一直是同一个线程在执行任务,达到了线程复用的目的

2、newFixedThreadPool

public class Test {public static void main(String[] args) throws InterruptedException {//创建一个自定义上限的线程池,设置最大线程数量为3ExecutorService pool = Executors.newFixedThreadPool(3);//提交任务pool.submit(new MyRunnable());pool.submit(new MyRunnable());pool.submit(new MyRunnable());pool.submit(new MyRunnable());}
}

创建一个自定义线程数量上限的线程池,并设置最大线程数量不超过3,然后我们提交了四个任务,输出结果中只有三个线程在工作

四、自定义线程池

我们在上面的快速上手中采用Executors工具类来创建的线程池,我们也能创建自定义的线程池,创建ThreadPoolExecutor类并通过构造方法的参数来初始化这个线程池,下面来介绍下这个方法的各种参数。

为了方便理解各个参数的意义,这里我们举个例子来介绍:假设有个餐厅,餐厅里有三个正式员工和三个临时员工,正式员工永远都不会被开除,临时员工如果餐厅一直没有顾客,那就会被开除。这个餐厅每个员工只能服务一位顾客,只有当前服务的顾客离开之后,才能服务另一位顾客,当这六名员工都在服务顾客的时候,此时如果再有新来的顾客,那么就应该在店门口排队,当排队人数到达一定的数量时,这个餐厅为了能确保每个人都能吃上饭,所以它就规定队伍人数达到上限的时候就不再接受新来的顾客了。

参数一:核心线程数量 int corePoolSize

  • 核心线程意思是在这个线程池中永远不会被销毁的线程,好比上面例子里的正式员工,初创线程池的时候是没有线程的,当有任务陆续来临的时候,线程池开始创建核心线程,但是核心线程数目具有上限。

参数二:线程池中最大线程数量 int maximumPoolSize 

  • 最大线程数量 = 核心线程数量 + 临时线程数量

参数三:空闲时间(值)  long keepAliveTime

参数四:空闲时间(单位) TimeUnit unit

  • 空闲时间指的是临时线程在多长时间内没有被使用,就会被销毁

参数五:阻塞队列 BlockingQueue<Runnable> workQueue

  • 阻塞队列意思是当核心线程都在执行任务时,新提交的任务就要进入阻塞队列,我们可以在创建阻塞队列的时候定义队列的长度

参数六:线程工厂 ThreadFactory threadFactory

  • 线程工厂用来表示线程池通过什么方式创建线程

参数七:拒绝策略 RejectedExecutionHandler handler

  • 当线程池里的线程都在执行任务,并且阻塞队列中的任务数量已达上限,那么就会执行拒绝策略,来处理新提交的任务

任务拒绝策略有以下几种方式:

1.ThreadPoolExecutor.AbortPolicy  丢弃任务并抛出RejectedExecutionException异常(默认策略)

2.ThreadPoolExecutor.DiscardPolicy  丢弃任务但是不抛出异常(不推荐)

3.ThreadPoolExecutor.DiscardOldestPolicy  抛弃队伍中等待时间最久的任务,把当前任务加入

4.ThreadPoolExector.CallerRunsPolicy  调用任务的run方法,绕过线程池执行

五、自定义线程池工作流程

延续上面的例子,假如我定义了核心线程有3个临时线程有3个阻塞队列长度为3,这个时候来了10个任务,线程池处理的方式如下:

任务一、二、三都会被核心线程执行,然后任务四、五、六进入阻塞队列,任务七、八、九会被临时线程执行,任务十触发任务拒绝策略

                                                             2-线程池工作流程图

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,      //核心线程数量      6,      //最大线程数量                 60,     //空闲时间(值)                 TimeUnit.SECONDS,      //空闲时间(单位)new ArrayBlockingQueue<>(3),       //等待队列,队列长度为3Executors.defaultThreadFactory(),           //线程工厂new ThreadPoolExecutor.AbortPolicy()        //拒绝策略 );

http://www.xdnf.cn/news/990865.html

相关文章:

  • 统计学(第8版)——统计学基础统计抽样与抽样分布(考试用)
  • HarmonyOS中LazyForEach的优缺点
  • 在QT中使用OpenGL
  • Python 元组
  • 使用spring-ai-alibaba接入大模型
  • mysql基本操作语句 增删改查基础语法速查表
  • MTK-USB模式动态设置
  • VScode安装配置PYQT6
  • MS7200+MS1824 HD转AV/S-Video/VGA/YPbPr/RGB888/BT601、656/BT1120转换器
  • Pandas时间数据处理:从基础到进阶的实战指南
  • 利用高分辨率卫星遥感数据以更智能、更快速的方式勘测评估能源开采现场
  • 第四章 文件管理
  • 软件测试用例设计总结
  • Position Embedding 有哪些方式?
  • @Indexed原理与实战
  • Java大模型开发入门 (3/15): 拥抱官方标准 - 使用OpenAI官方Java SDK调用DeepSeek
  • 航电系统之轨迹克隆技术篇
  • pyvis报错AttributeError: ‘NoneType‘ object has no attribute ‘render‘
  • python打卡day51@浙大疏锦行
  • 期权末日轮实值期权盈利未平仓怎么办?
  • 【多模态/T5】[特殊字符] 为什么视频生成模型还在用T5?聊聊模型选择的学问
  • Windows版PostgreSQL 安装 postgis扩展
  • 大数据下的分页通用架构设计:从随机IO到顺序IO
  • Gartner<Reference Architecture Brief: Data Integration>学习心得
  • 嵌入式程序存储结构
  • HW中常态化反钓鱼训练的具体战略部署
  • 【网络】每天掌握一个Linux命令 - netperf
  • 6. TypeScript 函数
  • 提升集装箱及金属包装容器制造交付效率:数字化项目管理系统的核心优势
  • 异常谋杀案--Java异常处理篇