java基础进阶-线程池

1、线程池

        线程池就是一个可以复用线程的技术。

2、应用场景

         用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。

3、线程池工作原理

从工作队列中获取一个任务,执行任务,然后返回线程池并等待下一个任务。

 

4、创建线程池

        java在jdk5.0起提供了代表线程池的接口:ExecutorService。

  • 方式一:使用ExecutService的实现类ThreadPoolExecutor自创建一个线程池对象。
  • 方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象。

 4.1、ThreadPoolExecutor

  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

参数说明:

  • 参数一:corePoolSize:指定线程池的核心线程数量。
  • 参数二:maximumPoolSize:指定线程池的最大线程数量。
  • 参数三:keepAliveTime:指定临时线程的存活时间。
  • 参数四:unit:指定临时线程存活的时间单位(秒,分,时,天)
  • 参数五:workQueue:指定线程池的任务队列
  • 参数六:threadFactory:指定线程池的线程工厂
  • 参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了怎么处理)
//1、通过ThreadPoolExecutor来创建线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 8,TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

 注意事项 

1、临时线程什么时候创建

        新任务提交时,发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。

2、什么时候开始会拒绝新任务

        核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。 

方法名称说明
void execute(Runnable command)执行Runnable任务
Future<T> submit(Callable<T> task)执行Callable任务,返回未来任务对象,用于获取线程返回的结果
void shutdown()等全部任务执行完毕后,再关闭线程池
List<Runnable> shutdownNow()立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务

5、线程池处理Runnable任务

public class ThreadPoolTest1 {public static void main(String[] args) {//1、通过ThreadPoolExecutor来创建线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 8,TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());MyRunnable myRunnable = new MyRunnable();//线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(myRunnable);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(myRunnable);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(myRunnable);//复用前面的核心任务pool.execute(myRunnable);//复用前面的核心任务pool.execute(myRunnable);//增加线程,超出任务队列
//        pool.execute(myRunnable);
//        pool.execute(myRunnable);//线程池中的三个主线程已经被占用,线程队列已满//此时再创建线程,就会创建临时线程
//        pool.execute(myRunnable);//等线程任务结束后关闭线程池pool.shutdown();//立即关闭线程池
//        pool.shutdownNow();}
}public class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"===>666");try {Thread.sleep(2000);//设置休眠时间,便于查看
//            Thread.sleep(Integer.MAX_VALUE);//设置最大时间,用来测试临时线程的创建} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

执行结果

pool-1-thread-1===>666
pool-1-thread-3===>666
pool-1-thread-2===>666
pool-1-thread-1===>666
pool-1-thread-2===>666

5.1、临时线程创建时机

public class ThreadPoolTest1 {public static void main(String[] args) {//1、通过ThreadPoolExecutor来创建线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 8,TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());MyRunnable myRunnable = new MyRunnable();//线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(myRunnable);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(myRunnable);//线程池会自动创建一个新线程,自动处理这个任务,自动执行的pool.execute(myRunnable);//复用前面的核心任务pool.execute(myRunnable);//复用前面的核心任务pool.execute(myRunnable);//增加线程,超出任务队列pool.execute(myRunnable);pool.execute(myRunnable);//线程池中的三个主线程已经被占用,线程队列已满//此时再创建线程,就会创建临时线程pool.execute(myRunnable);//等线程任务结束后关闭线程池
//        pool.shutdown();//立即关闭线程池
//        pool.shutdownNow();}
}
public class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"===>666");try {
//            Thread.sleep(2000);//设置休眠时间,便于查看Thread.sleep(Integer.MAX_VALUE);//设置最大时间,用来测试临时线程的创建} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

执行结果:会创建临时线程4

pool-1-thread-1===>666
pool-1-thread-3===>666
pool-1-thread-2===>666
pool-1-thread-4===>666

如果再加一个,就会创建临时线程5

pool-1-thread-2===>666
pool-1-thread-4===>666
pool-1-thread-3===>666
pool-1-thread-1===>666
pool-1-thread-5===>666

再增加一个,就会超出线程池的限定,此时就会按照设置的方法进行处理

参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了怎么处理)

新任务拒绝策略:

策略详解
ThreadPoolExecutor.AbortPolicy()
不执行此任务,而且直接抛出一个运行时异常
ThreadPoolExecutor.DiscardPolicy()
新任务被提交后直接被丢弃掉,并且不会抛出异常,无法感知到这个任务会被丢弃,可能造成数据丢失。
ThreadPoolExecutor.DiscardOldestPolicy()
会丢弃任务队列中的头结点,通常是存活时间最长并且未被处理的任务。
ThreadPoolExecutor.CallerRunsPolicy()
当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务。不会抛出异常。

6、线程池处理Callable任务

public class ThreadPoolTest2 {public static void main(String[] args) throws ExecutionException, InterruptedException {//1、通过ThreadPoolExecutor来创建线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 8,TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());Future f1 = pool.submit(new MyCallable(100));Future f2 = pool.submit(new MyCallable(200));Future f3 = pool.submit(new MyCallable(300));Future f4 = pool.submit(new MyCallable(400));System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());}
}public class MyCallable implements Callable {private int n;public MyCallable(int n) {this.n = n;}@Overridepublic String call() throws Exception {int sum = 0;for (int i = 1; i <= n; i++) {sum += i;}return Thread.currentThread().getName()+"求出了1-"+n+"的和是:" + sum;}
}

执行结果:

pool-1-thread-1求出了1-100的和是:5050
pool-1-thread-2求出了1-200的和是:20100
pool-1-thread-3求出了1-300的和是:45150
pool-1-thread-3求出了1-400的和是:80200

7、Executors工具类实现线程池

方法名称说明
newFixedThreadPool(int nThreads)创建固定数量线程的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。
newSingleThreadExecutor()

创建只有一个线程的线程池对象,如果改线程出现异常而结束,那么线程池会补充一个新线程。

newCachedThreadPool线程数量随着人物增加而增加,如果线程任务执行完毕且空闲了60秒则会被回收掉
newScheduledThreadPool创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务
public class ThreadPoolTest3 {public static void main(String[] args) throws ExecutionException, InterruptedException {//1、通过ThreadPoolExecutor来创建线程池
//        ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 8,
//                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
//                new ThreadPoolExecutor.CallerRunsPolicy());//        ExecutorService pool = Executors.newFixedThreadPool(3);
//        ExecutorService pool = Executors.newSingleThreadExecutor();
//        ExecutorService pool = Executors.newCachedThreadPool();ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);Future f1 = pool.submit(new MyCallable(100));Future f2 = pool.submit(new MyCallable(200));Future f3 = pool.submit(new MyCallable(300));Future f4 = pool.submit(new MyCallable(400));System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());}
}

8、核心线程数的配置

  • 计算密集型的任务:核心线程数量=CPU核数 +1
  • IO密集的任务:核心数量=CPU核数 * 2

9、不建议使用Executors创建线程池

  1. 缺乏对线程池的精细控制Executors 提供的方法通常创建一些简单的线程池,如固定大小的线程池、单线程线程池等。然而,这些线程池的配置通常是有限制的,难以进行进一步的定制和优化。

  2. 可能引发内存泄漏:一些 Executors 创建的线程池,特别是 FixedThreadPool 和 SingleThreadExecutor,使用无界队列来存储等待执行的任务。这意味着如果任务提交速度远远快于任务执行速度,队列中可能会积累大量未执行的任务,可能导致内存泄漏。

  3. 不易处理异常Executors 创建的线程池默认使用一种默认的异常处理策略,通常只会将异常打印到标准输出或记录到日志中,但不会提供更多的控制。这可能导致异常被忽略或无法及时处理。

  4. 不支持线程池的动态调整:某些线程池应该支持动态调整线程数量以应对不同的负载情况。Executors 创建的线程池通常是固定大小的,不容易进行动态调整。

  5. 可能导致不合理的线程数目:一些 Executors 方法创建的线程池默认将线程数目设置为非常大的值,这可能导致系统资源的浪费和性能下降。

因此,对于生产环境中的应用程序,通常建议使用 ThreadPoolExecutor 类直接创建和配置线程池,以便更精确地控制线程池的各个参数,包括核心线程数、最大线程数、队列类型、拒绝策略等。这样可以更好地满足应用程序的需求,并确保线程池在不同负载情况下表现良好。当然,在某些情况下,Executors 创建的简单线程池可能足够使用,但需要谨慎考虑其限制和适用性。

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

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

相关文章

单片机学习1——点亮一个LED灯

Keil软件编写程序&#xff1a; 特殊功能寄存器声明&#xff1a; #include<reg52.h>sbit LED P1^0;void main() {LED 0;while(1); } 代码说明&#xff1a; sbit 语句是特殊功能位声明。 生成HEX文件&#xff0c;这个文件是下载到单片机里的文件。Options for Target…

【Linux】初识重定向(输入输出)

一切皆文件 这是Linux的设计理念&#xff0c;因为这个理念的存在我们可以使用统一的方法对待不同的东西&#xff0c;&#xff0c;这也是为什么嵌入式之类的会需要Linux&#xff0c;因为用LInux来操纵硬件真的很方便 另外我们下文也会都基于这个理念来命名&#xff0c; 比如&am…

MySQL(免密登录)

简介: MySQL免密登录是一种允许用户在没有输入密码的情况下直接登录到MySQL服务器的配置。这通常是通过在登录时跳过密码验证来实现的。 1、修改MySQL的配置文件 使用vi /etc/my.cnf&#xff0c;添加到【mysqld】后面 skip-grant-tables #配置项告诉mysql跳过权限验证&#…

[Spring ~必知必会] Bean 基础常识汇总

文章目录 Bean 相关到底什么是beanFactorybeanFactory能干啥ApplicationContext是什么ApplicationContext的功能比 BeanFactory多了什么 容器的实现BeanFactory的实现ApplicationContext的实现xml 配置配置类配置 Bean 的生命周期3.1 Bean 的常见的后处理器测试代码总结 3.2 工…

python回溯求解电话号码组合

给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 输入&#xff1a;digits "23" 输出&#xff1a;["ad&qu…

振南技术干货集:FFT 你知道?那数字相敏检波 DPSD 呢?(2)

注解目录 1 、DPSD 的基础知识 1.1 应用模型 1.2 原理推导 1.3 硬件 PSD &#xff08;相敏检波&#xff0c;就是从繁乱复杂的信号中将我们关心的信号检出来&#xff0c;同时对相位敏感。 数学原理&#xff0c;逃不掉的&#xff0c;硬着头皮看吧。&#xff09; 2 、DPSD …

【Redis缓存】RedisTemplate如何获取符合要求的key,批量获取key

RedisTemplate如何获取符合要求的key,批量获取key 一、方法/命令二、数据使用 一、方法/命令 如果使用命令的形式&#xff0c;输入以下命令即可 keys *如果使用RedisTemplate&#xff0c;则方法为 redisTemplate.keys()获取所有符合条件的key。 二、数据使用 redis中缓存了…

linux下的工具---vim

一、了解vim 1、vim是linux的开发工具 2、vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅可以在终端运行…

Android笔记(十四):JetPack Compose中附带效应(一)

在Android应用中可以通过定义可组合函数来搭建应用界面。应用界面的更新往往是与可组合函数内部定义的状态值相关联的。当界面的状态值发生变更&#xff0c;会导致应用界面进行更新。在Android笔记&#xff08;九&#xff09;&#xff1a;Compose组件的状态&#xff0c;对Compo…

【新手解答2】深入探索 C 语言:一些常见概念的解析

C语言的相关问题解答 写在最前面问题1变量名是否有可能与变量重名&#xff1f;变量名和变量的关系变量名与变量是否会"重名"举例说明结论 变量则是一个地址不变&#xff0c;值时刻在变的“具体数字”变量的地址和值变量名与数据类型具体化示例结论 问题2关于你给我的…

priority_queue模拟实现

目录 仿函数 模拟实现 结果 大根堆 小根堆 完整代码 priority_queue.h test.c 仿函数 仿函数的通俗定义&#xff1a;仿函数&#xff08;functor&#xff09;又称为函数对象&#xff08;function object&#xff09;是一个能行使函数功能 的类。仿函数的语法几乎和我们…

ubuntu修改系统语言

修改ubuntu系统语言 操作指令修改系统设置总结 操作 ubuntu系统自带的英文环境&#xff0c;个人觉得用起来不方便。改掉吧。换成中文 指令修改 参考了一些博客的解决方式 ctrlartT 打开终端。 sudo apt-get install language-pack-zh-hans 输入下载汉化包的指令。 但是&…

MATLAB的rvctools工具箱熟悉运动学【机械臂机器人示例】

1、rvctools下载安装 rvctools下载地址&#xff1a;rvctools下载 截图如下&#xff0c;点击红色箭头指示的“Download Shared Folder” 即可下载 下载之后进行解压&#xff0c;解压到D:\MATLAB\toolbox这个工具箱目录&#xff0c;这个安装路径根据自己的情况来选择&#xff0c…

OSG编程指南<三>:利用Cmake+VS2019+OSG3.6.5编译osgVerse

目前osgearth的ralease版本和debugx64都已经编译过了并且自测可用&#xff0c;放到资源里osgearth下载&#xff0c;供需要的朋友下载参考。环境配置&#xff1a;win10VS2017OSG3.6.4OSGEarth2.10.2&#xff0c; 1、配置OSG环境 编译好osg&#xff0c;然后在系统变量中配置如下…

javascript 运算符

javascript 运算符 目录 javascript 运算符 一、算术运算符 1、自增运算符 2、自减运算符 二、比较运算符 三、赋值运算符 四、逻辑运算符 五、条件运算符 疑难解答&#xff1a; 这一节&#xff0c;我们来介绍JavaScript的运算符。运算符是完成一系列操作的符号&…

Rocket架构

NameServer&#xff1a;作为注册中心&#xff0c;提供路由注册、路由踢出、路由发现功能&#xff0c;舍弃强一致&#xff0c;保证高可用&#xff0c;集群中各个节点不会实时通讯&#xff0c;其中一个节点下线之后&#xff0c;会提供另外一个节点保证路由功能。 Rocket mq name…

基于Python获取亚马逊的评论信息的处理

文章目录 一、分析亚马逊的评论请求二、获取亚马逊评论的内容三、亚马逊评论信息的处理四、代码整合4.1 代理设置4.2 while循环翻页 总结关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具…

力扣hot100 和为 K 的子数组 前缀和

&#x1f468;‍&#x1f3eb; 题目地址 &#x1f37b; AC code class Solution {public int subarraySum(int[] nums, int k){int ans 0;int n nums.length;int[] s new int[n 1];// 前缀和s[0] 0;s[1] nums[0];for (int i 2; i < n; i)s[i] s[i - 1] nums[i - 1…

C#-基础及扩展合集(持续更新)

一、基础 Ⅰ 关键字 1、record record&#xff08;记录&#xff09;&#xff0c;编译器会在后台创建一个类。支持类似于结构的值定义&#xff0c;但被实现为一个类&#xff0c;方便创建不可变类型&#xff0c;成员在初始化后不能再被改变 &#xff08;C#9新增&#xff09; …

PostgreSQL + SQL Server = WiltonDB

WiltonDB 是一个基于 PostgreSQL 的开源数据库&#xff0c;通过 Babelfish 插件支持 Microsoft SQL Server 协议以及 T-SQL 语句。 Babelfish 是亚马逊提供的一个开源项目&#xff0c;使得 PostgreSQL 数据库同时具有 Microsoft SQL Server 数据查询和处理的能力。Babelfish 可…