当前位置: 首页 > 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()        //拒绝策略 );

相关文章:

  • AIStor 的模型上下文协议 (MCP) 服务器:管理功能
  • Pandas:你的数据分析瑞士军刀![特殊字符]✨
  • Unity UGUI GraphicRaycaster.Raycast详解
  • Appium + Node.js 测试全流程
  • 去中心化交易所(DEX)架构:智能合约驱动与AMM算法创新
  • 金仓数据库主备集群故障自动转移技术解析
  • 新能源知识库(39)261度电储能柜成为当前市场主流原因分析
  • 探究:什么是扁平化组织?有什么益处?
  • Element:Table表头全部或单个表头颜色header-row-style
  • ABB 500BIM01 1MRB150024R0002
  • 鹰盾视频加密器播放器Win32系统播放器兼容开发的技术要点与实践指南
  • STM32H723的SPI配置及简单使用!
  • AI 视频创作技术全解析:从环境搭建到实战落地​
  • 一起学习swin-transformer(一)
  • JAVASE:方法
  • 前端基础知识ES6系列 - 01(var、let、const之间的区别)
  • AI+预测3D新模型百十个定位预测+胆码预测+去和尾2025年6月11日第105弹
  • 【行云流水AI笔记】游戏里面的强化学习使用场景
  • deepbayes: VI回顾和GMM近似推断
  • Wordpress安装插件提示输入ftp问题解决
  • 如何做好网站推广优化/我赢网seo优化网站
  • 西宁网站seo/百度工具seo
  • 优酷网站谁做的/朋友圈推广平台
  • 软件开发学院/百度搜索引擎seo
  • 免费网站素材下载/网络广告案例以及分析
  • 怎么自己购买域名 建设网站/如何做网络推广外包