JUC--线程池

目录

一、线程池的介绍

二、线程池的创建

 三、特殊线程池

3.1.CompletionService异步处理

3.2.ThreadPoolExecutor

 3.3 ForkJoinPool


        虽然多线程的技术大大帮助了程序运行的效率,但是在太多的线程的创建与销毁下,系统的开销也将会是非常庞大的。所以为了实现线程的可管理性,并且降低开销。则JUC中提供了线程池的概念,以及相关实现方法

一、线程池的介绍

面试题:线程池的实现

创建一个阻塞队列来容纳任务,在第一次执行任务时创建足够多的线程,并处理任务,之后每个工作线程自动从任务队列中获取线程,直到任务队列中任务为0为止,此时线程处于等待状态,一旦有工作任务加入任务队列中,即刻唤醒工作线程进行处理,实现线程的可复用性。

创建四种线程池的方法

方法名描述
newCachedThreadPool()创建一个可以根据需要自动调整线程数量的线程池。
newFixedThreadPool(int nThreads)创建具有固定数量线程的线程池。
newSingleThreadScheduledExecutor()创建仅包含单个线程的线程池,可以按照计划安排任务的执行。
newScheduledThreadPool(int corePoolSize)创建具有固定核心线程数量的线程池,可以按照计划安排任务的执行,并且具有一定数量的可变大小线程池。

四种线程池的作用

  • newCachedThreadPool():创建一个可以根据需要自动调整线程数量的线程池。适用于执行大量短期异步任务的场景,线程池会根据任务的数量自动增加或减少线程的数量。

  • newFixedThreadPool(int nThreads):创建具有固定数量线程的线程池。适用于需要控制并发线程数的场景,线程数量固定不变。

  • newSingleThreadScheduledExecutor():创建仅包含单个线程的线程池,可以按照计划安排任务的执行。适用于需要按顺序执行任务的场景,每个任务都会在前一个任务完成后再执行。

  • newScheduledThreadPool(int corePoolSize):创建具有固定核心线程数量的线程池,可以按照计划安排任务的执行,并且具有一定数量的可变大小线程池。适用于需要根据计划安排任务执行时间的场景,核心线程数固定,但可以根据需要增加额外的线程来处理更多任务。

创建线程池

Executor类创建的线程池主要通过两类接口描述:ExecutorService(线程池)和ScheduledExecutorService(调度线程池)

线程池常用的方法

方法名描述
execute(Runnable command)按顺序执行给定的命令,提交到线程池中进行执行。
submit(Runnable task)提交一个可运行的任务给线程池,并返回一个表示该任务的未决结果的 Future。
submit(Callable task)提交一个可调用的任务给线程池,并返回一个表示该任务的未决结果的 Future。
invokeAll(Collection<? extends Callable<T>> tasks)在给定的任务列表中的所有任务完成之前,按顺序执行并等待每个任务的完成,并返回一个表示所有任务结果的 Future 列表。
invokeAny(Collection<? extends Callable<T>> tasks)执行给定的任务列表,返回其中一个已经成功完成的任务的结果,并取消所有其他任务。
isShutdown()如果线程池已经调用了 shutdown()shutdownNow() 方法,返回 true。否则返回 false
shutdown()平滑地关闭线程池,停止接受新任务,并尝试将所有未完成的任务继续执行。
schedule()安排在给定的延迟后执行任务,并返回一个表示该任务的未决结果的 ScheduledFuture

二、线程池的创建

面试题:线程池中的线程是怎么创建的?

四种线程对象的创建

1.案例代码:创建缓存线程池(建立了一个线程池,而且线程数量是没有限制的(当然,不能超过Integer的最大值),新增一个任务即有一个线程处理,或者复用之前空闲的线程,或者重亲启动一个线程,但是一旦一个线程在60秒内一直处于等待状态时(也就是一分钟无事可做),则会被终止)

package Example2135;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class javaDemo {public static void main(String[] args) {ExecutorService executorService = Executors.newCachedThreadPool();
//        如果线程池中的线程不够用,则会自动新创建线程,线程最多为Integer.MAX_VALUE个for (int i=0;i<100;i++){executorService.submit(()->{System.out.println(Thread.currentThread().getId()+"--"+Thread.currentThread().getName());});}if (!executorService.isShutdown()){executorService.shutdown();}}
}

注意:关于execute与submit方法的关系

  1. 返回值类型:execute() 方法没有返回值,仅用于提交可运行的任务;而 submit() 方法将提交的任务封装成一个 Future 对象,可以通过该对象获取任务执行的结果。

  2. 参数类型:execute() 方法接受一个 Runnable 类型的参数,用于提交不需要返回结果的任务;而 submit() 方法可以接受 Runnable 或者 Callable 类型的参数,用于提交既可以返回结果也可以不返回结果的任务。

  3. 异常处理:execute() 方法无法捕获任务执行过程中的异常,如果任务抛出异常,线程池无法感知;而 submit() 方法可以捕获任务执行过程中的异常,并以 Future 形式返回异常信息。

综上所述,execute() 更适合用于执行简单的、无需返回结果的任务,而 submit() 则更灵活,可以用来执行有返回结果的任务,并且可以捕获任务的异常。

 2.案例代码:创建固定长度的线程池(在初始化时已经决定了线程的最大数量,若任务添加的能力超出了线程的处理能力,则建立阻塞队列容纳多余的任务)

package Example2136;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class javaDemo {public static void main(String[] args) {
//    创建固定长度为4的线程池ExecutorService executorService = Executors.newFixedThreadPool(4);
//           让线程输出自己的默认名称for(int i=0;i<10;i++){executorService.execute(()->{System.out.println(Thread.currentThread().getName());});}executorService.shutdown();}}

3.案例代码:创建单线程(顾名思义就是一个池中只有一个线程在运行,该线程永不超时,而且由于是一个线程,当有多个任务需要处理时,会将它们放置到一个无界阻塞队列中逐个处理)

package Example2137;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class javaDemo {public static void main(String[] args) {
//        创建单线程对象ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();for (int i=0;i<10;i++){executorService.submit(()->{System.out.println(Thread.currentThread().getName());});}executorService.shutdown();}
}

 4.案例代码:创建调度线程池

package Example2139;import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class javaDemo {public static void main(String[] args) {
//        创建调度线程池,并设置内核线程数量为1ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
//        设置调度的任务,并设置3秒后执行,之后每隔2秒执行一次scheduledExecutorService.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {Date date = new Date();System.out.println(Thread.currentThread().getName()+"为您播报:当前时间为"+ date);}},3,2, TimeUnit.SECONDS);}
}

如果在线程池中传入了Callable接口实例,那么可以通过Future接口获取返回的结果在ExecutorService接口中提供了invokeAny和invokeAll两个方法可以实现一组Callable实例的执行

案例代码:执行一组Callable实例

package Example2140;import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.*;public class javDemo {public static void main(String[] args) throws InterruptedException, ExecutionException {
//        保存多个线程对象Set<Callable<String>> allThread = new HashSet<Callable<String>>();
//        集合中追加线程for (int i=0;i<10;i++){final int temp = i;allThread.add(()->{return Thread.currentThread().getName()+temp;});}
//        创建固定长度的线程ExecutorService service = Executors.newFixedThreadPool(3);
//        使用Future接收类型List<Future<String>> list = service.invokeAll(allThread);for (Future<String> future : list){System.out.println(future.get());}}
}


 三、特殊线程池

3.1.CompletionService异步处理

CompletionService的主要功能是可以通过异步处理获取到线程池返回的结果,CompletionService可以接收Callable或者Runnable实现的线程任务。并且可以通过ExecutorCompletionService子类实例化接口对象

什么是异步处理

异步处理指的是在任务执行期间,不需要等待任务完成就可以继续执行其他操作。通过异步处理,可以提高程序的响应速度和效率。在传统的同步处理中,必须等待一个任务执行完毕后才能执行下一个任务,而异步处理则可以同时执行多个任务,提高了任务的并发性。

案例:使用CompletionService接口获取异步执行任务结果

package Example2141;import java.util.concurrent.*;//线程体
class ThreadItem implements Callable<String> {@Overridepublic String call() throws Exception {//        获取当前时间戳long TimeMillis = System.currentTimeMillis();try {System.out.println("[start]"+Thread.currentThread().getName());TimeUnit.SECONDS.sleep(3);System.out.println("[end]"+Thread.currentThread().getName());}catch (Exception e){}return Thread.currentThread().getName()+":"+ TimeMillis;}
}public class javaDemo {public static void main(String[] args) throws InterruptedException, ExecutionException {ExecutorService executorService = Executors.newCachedThreadPool();CompletionService<String> completionService = new ExecutorCompletionService<String>(executorService);
//        信息生产者for (int i=0;i<10;i++){
//            提交线程completionService.submit(new ThreadItem());}for (int i=0;i<10;i++){System.out.println("获取数据"+completionService.take().get());}
//        关闭线程池executorService.shutdown();}
}

3.2.ThreadPoolExecutor

通过Executors类可以进行实现线程池的创建,而通过Executors类的创建都是基于ThreadPoolExecutor类的实现创建。在一些特殊的环境下,开发者也可以直接使用ThreadPoolExecutor类结合阻塞队列与拒绝策略实现属于自己的线程池。

什么是拒绝策略

拒绝策略(Rejection Policy)是在线程池中,当提交的任务超过线程池容量且无法处理时,决定如何处理这些任务的一种策略。当线程池中的工作队列(阻塞队列)已满,并且没有空闲的线程可以执行任务时,就会触发拒绝策略。

四种拒绝策略

拒绝策略描述
AbortPolicy默认策略,当线程池无法处理新任务时,直接抛出RejectedExecutionException异常,表示拒绝执行该任务。
CallerRunsPolicy当线程池无法处理新任务时,将任务返回给调用者进行处理。也就是由调用submit方法的线程来执行该任务。
DiscardOldestPolicy当线程池无法处理新任务时,会丢弃最早进入工作队列的任务,并尝试重新提交当前任务。
DiscardPolicy当线程池无法处理新任务时,会默默丢弃无法处理的任务,不给予任何提示。该策略可能会导致任务丢失,慎用。

案例代码:

package Example2142;import java.util.concurrent.*;public class javaDemo {public static void main(String[] args) {BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
//        通过ThreadPoolExecutor创建线程池,该线程有2个内核线程,最大线程量为2,每个线程存活时间为6秒,使用默认拒绝策略ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,2,6L, TimeUnit.SECONDS,queue, Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());for (int i=0;i<5;i++){threadPoolExecutor.submit(()->{System.out.println("[BEFORE]"+Thread.currentThread().getName());try {TimeUnit.SECONDS.sleep(2);}catch (Exception  e){}System.out.println("[AFTER]" +Thread.currentThread().getName());});}}
}

 3.3 ForkJoinPool

在JDK1.7以后为了充分利用多核CPU的性能优势,可以将一个复杂的业务计算进行拆分,交由多台CPU并行计算,这样用以提高程序的执行性能,所以引入了ForkJoinPool类,该类包含两个操作

Fork(分解操作):将一个大型业务拆分成多个小的任务放在框架中执行

Join(合并操作):主任务将等待多个子任务完成后进行合并

在ForkJoinPool中有两个子类,RecursiveTask(有返回值的任务)、RecursiveAction(无返回值的任务)

该类常用的方法:

方法说明
fork()将任务分解为更小的子任务并异步执行。将当前任务放入工作队列中,以供其他工作线程获取并执行。
join()等待当前任务的执行结果,并返回结果。如果任务还没有完成,则调用线程会被阻塞,直到任务完成后才会继续执行。
isCompleteNormally()判断任务是否已经正常完成。如果任务在完成时没有抛出异常,该方法返回true;如果任务被取消或者发生异常,返回false
invokeAll(tasks)执行给定的任务集合,并等待所有任务完成。该方法会将任务集合拆分为更小的子任务,并由线程池中的工作线程异步执行。
getException()获取任务执行过程中所发生的异常。如果任务正常完成或者尚未完成,该方法返回null;如果任务被取消或者异常中止,返回引发异常的原因。

案例代码:

package Example2143;import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;class SumTask extends RecursiveTask<Integer> {private int start;private int end;public SumTask(int start, int end) {this.start = start;this.end = end;}@Overrideprotected Integer compute() {int sum = 0;if (this.end - this.start < 100) {for (int x = this.start; x <= this.end; x++) {sum += x;}} else {int middle = (start + end) / 2;SumTask leftTask = new SumTask(this.start, middle);SumTask rightTask = new SumTask(middle + 1, this.end);leftTask.fork();rightTask.fork();// 等待子任务完成并将结果相加sum = leftTask.join() + rightTask.join();}return sum;}
}public class javaDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {SumTask task = new SumTask(0,100);ForkJoinPool pool = new ForkJoinPool();Future<Integer> future = pool.submit(task);System.out.println(future.get());}
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/101839.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

stack和queue的模拟实现

stack和queue的模拟实现 容器适配器什么是适配器STL标准库中stack和queue的底层结构deque的简单介绍deque的缺陷 stack模拟实现queue模拟实现priority_queuepriority_queue的使用priority_queue的模拟实现 容器适配器 什么是适配器 适配器是一种设计模式(设计模式是一套被反复…

Python土力学与基础工程计算.PDF-隧道涌水量

Python 求解代码如下&#xff1a; 1. # 定义参数 2. A 2000 # 地表面积&#xff0c;单位&#xff1a;平方米 3. S 10 # 截面积&#xff0c;单位&#xff1a;平方米 4. h 500 # 年地下径流深度&#xff0c;单位&#xff1a;毫米 5. 6. # 转换单位 7. h h / 1000 # 单…

基于51单片机直流电机PWM调速液晶1602显示设计

一、系统方案 本文主要研究了利用MCS-51系列单片机控制PWM信号从而实现对直流电机转速进行控制的方法。本文中采用了三极管组成了PWM信号的驱动系统&#xff0c;并且对PWM信号的原理、产生方法以及如何通过软件编程对PWM信号占空比进行调节&#xff0c;从而控制其输入信号波形等…

Java面向对象三大特性之多态及综合练习

1.1 多态的形式 多态是继封装、继承之后&#xff0c;面向对象的第三大特性。 多态是出现在继承或者实现关系中的。 多态体现的格式&#xff1a; 父类类型 变量名 new 子类/实现类构造器; 变量名.方法名(); 多态的前提&#xff1a;有继承关系&#xff0c;子类对象是可以赋…

无线上网连接及配置

目录 1. 无线上网连接及配置 1.1 无线路由器连接方式 ​编辑 1.2 无线路由器的基本配置 1.配置用户计算机上的IP地址 2.访问无线路由Web管理界面 1.3 WAN 口设置 1.动态 IP 2.静态 IP 1. 无线上网连接及配置 一小型公司共有20名员工。由于公司业务需要访问Internet&…

考研C语言进阶题库——更新41-50题

目录 41.编写程序要求输出整数a和b若a和b的平方和大于100&#xff0c;则输出a和b的平方和&#xff0c;否则输出a和b的和 42.现代数学的著名证明之一是Georg Cantor证明了有理数是可枚举的。他是用下面这一张表来证明这一命题的&#xff1a;第一项是1/1&#xff0c;第二项是是…

【CAM】CAM(Class Activation Mapping)——可视化CNN的特征定位

文章目录 一、CAM(Class Activation Mapping)二、CAM技术实现2.1 网络修改2.2 微调2.2 特征提取 三、总结Reference 完整代码见Github &#xff1a;https://github.com/capsule2077/CAM-Visualization &#xff0c;如果有用可以点个Star&#xff0c;谢谢&#xff01; 一、CAM(C…

RSU交叉工具链安装不可用

1、在安装完交叉工具链去编译程序的时候&#xff0c;提示交叉工具链的命令找不到&#xff0c;检查各种路径配置其实都是配置好了&#xff0c;就是不行。 这是需要用下面的命令去更新一下交叉工具链&#xff0c;当然/opt目录下需要安装好对应文件 目前发现金溢、中兴的V2X交叉工…

链表的顶级理解

目录 1.链表的概念及结构 2.链表的分类 单向或者双向 带头或者不带头 循环或者非循环 3.无头单向非循环链表的实现 3.1创建单链表 3.2遍历链表 3.3得到单链表的长度 3.4查找是否包含关键字 3.5头插法 3.6尾插法 3.7任意位置插入 3.8删除第一次出现关键字为key的节点 …

【3Ds Max】可编辑多边形“点”层级的简单使用

目录 简介 示例 &#xff08;1&#xff09;移除 &#xff08;2&#xff09;断开 &#xff08;3&#xff09;焊接 &#xff08;4&#xff09;挤出 &#xff08;5&#xff09;切角 &#xff08;6&#xff09;目标焊接 &#xff08;7&#xff09;连接 简介 在3ds Max中&…

CH583/2构建工程教程

CH583/2构建工程教程 绪论资源移植步骤准备移植步骤一步骤二 工程配置修改工程名修改前修改后 工程配置修改资源文件 修改C/C general修改C/C构建修改汇编交叉编译修改C交叉编译修改GNU RISC-V Cross Linker 修改跟编译 移植注意事项 绪论 资源 CH583/2的SDK下载 移植步骤 …

昨晚做梦面试官问我三色标记算法

本文已收录至GitHub&#xff0c;推荐阅读 &#x1f449; Java随想录 微信公众号&#xff1a;Java随想录 原创不易&#xff0c;注重版权。转载请注明原作者和原文链接 文章目录 三色标记算法增量更新原始快照 某天&#xff0c;爪哇星球上&#xff0c;一个普通的房间&#xff0c…

基于Linux操作系统中的shell脚本

目录 前言 一、概述 1、什么是shell&#xff1f; 2、shell脚本的用途有哪些&#xff1f; 3、常见的shell有哪些&#xff1f; 4、学习shell应该从哪几个方面入手&#xff1f; 4.1、表达式 1&#xff09;变量 2&#xff09;运算符 4.2、语句 1&#xff09;条件语句&am…

HIDS-wazuh 的配置和防御

目录 安装wazuh 常用内容 检测sql注入 主动响应 安装wazuh 本地测试的话建议用ova文件&#xff0c;直接导入虚拟机就能用了 官网&#xff1a;Virtual Machine (OVA) - Installation alternatives 常用内容 目录位置&#xff1a;/etc/ossec 配置文件&…

【自动化剧本】Role角色

目录 一、Roles模块1.1roles的目录结构1.2roles 内各目录含义解释1.3在一个 playbook 中使用 roles 的步骤 二、使用Role编写LNMP剧本2.1 搭建Nginx角色2.2搭建Mysql角色2.3搭建php角色2.4lnmp剧本 一、Roles模块 roles用于层次性、结构化地组织playbook。roles能够根据层次型结…

【从零学习python 】75. TCP协议:可靠的面向连接的传输层通信协议

文章目录 TCP协议TCP通信的三个步骤TCP特点TCP与UDP的区别TCP通信模型进阶案例 TCP协议 TCP协议&#xff0c;传输控制协议&#xff08;英语&#xff1a;Transmission Control Protocol&#xff0c;缩写为 TCP&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议…

Oracle-rolling upgrade升级19c

前言: 本文主要描述Oracle11g升19c rolling upgrade升级测试&#xff0c;通过逻辑DGautoupgrade方式实现rolling upgrade&#xff0c;从而达到在较少停机时间内完成Oracle11g升级到19c的目标 升级介绍&#xff1a; 升级技术: rolling upgrade轮询升级&#xff0c;通过采用跨版…

项目实战笔记2:硬技能(上)

序&#xff1a; 本节串讲了项目管理硬技能&#xff0c;有些术语可以结合书或者网上资料来理解。没有想书上讲的那样一一列举。 做计划 首先强调为什么做计划&#xff1f; 计划就是各个角色协同工作的基准&#xff08;后面做风险监控、进度的监控&#xff09;&#xff0c;贯穿于…

大数据及软件教学与实验专业实训室建设方案

一 、系统概述 大数据及软件教学与实验大数据及软件教学与实验在现代教育中扮演重要角色&#xff0c;这方面的教学内容涵盖了大数据处理、数据分析、数据可视化和大数据应用等多个方面。以下是大数据及软件教学与实验的一般内容&#xff1a;1. 数据基础知识&#xff1a;教授学生…

【C#学习笔记】匿名函数和lambda表达式

文章目录 匿名函数匿名函数的定义匿名函数作为参数传递匿名函数的缺点 lambda表达式什么是lambda表达式闭包 匿名函数 为什么我们要使用匿名函数&#xff1f;匿名函数存在的意义是为了简化一些函数的定义&#xff0c;特别是那些定义了之后只会被调用一次的函数&#xff0c;与其…