java基础进阶-线程池

news2024/9/24 19:20:47

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{
    @Override
    public 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{
    @Override
    public 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;
    }

    @Override
    public 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.coloradmin.cn/o/1263513.html

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

相关文章

2023年【起重机械指挥】考试题库及起重机械指挥考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年【起重机械指挥】考试题库及起重机械指挥考试资料&#xff0c;包含起重机械指挥考试题库答案和解析及起重机械指挥考试资料练习。安全生产模拟考试一点通结合国家起重机械指挥考试最新大纲及起重机械指挥考试真…

OpenSSL 使用AES对文件加解密

AES&#xff08;Advanced Encryption Standard&#xff09;是一种对称加密算法&#xff0c;它是目前广泛使用的加密算法之一。AES算法是由美国国家标准与技术研究院&#xff08;NIST&#xff09;于2001年发布的&#xff0c;它取代了原先的DES&#xff08;Data Encryption Stand…

设计师福利!2024在线图标设计网站推荐,不容错过的宝藏!

在当今竞争激烈的商业环境中&#xff0c;公司或个人品牌的视觉识别元素已经成为区分你和竞争对手的关键因素之一。一个独特而引人注目的标志可以深深扎根于人们的心中&#xff0c;并在消费者心中建立一个强烈的品牌印象。如果你正在寻找合适的工具来创建或改进你的标志&#xf…

Nginx系列-正向代理和反向代理

Nginx系列-正向代理和反向代理 文章目录 Nginx系列-正向代理和反向代理1. 三个对象2. 两种场景代理2.1. 正向代理2.2. 反向代理 3. 两种场景的对比3.1 为什么叫做反向代理3.2 正向代理和反向代理的作用 1. 三个对象 客户端&#xff1a;发出请求到代理&#xff0c;并接收代理的…

2019年11月20日 Go生态洞察:Go开发者调查启动

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

1128. 等价多米诺骨牌对的数量

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/number-of-equivalent-domino-pa…

主机的具体权限规划:ACL的使用

目的&#xff1a;针对某一用户或某一组来设置特定权限需求&#xff0c;针对上&#xff0c;接着设置 ACL可以针对单一用户&#xff0c;文件&#xff0c;或者目录来进行rwx的权限设置&#xff0c;对于需要特殊权限的设置非常有帮助。 第一&#xff0c;查看文件系统是否支持&…

frida - 2.hook使用

frida hook 方法 下面是frida客户端命令行的参数帮助 Frida两种操作模式 1.attach 模式 将一个脚本注入到 Android 目标进程,即需要App处于启动状态, 这意味着只能从 当前时机往后hook。 frida -U -l myhook.js com.xxx.xxxx参数解释: -U 指定对USB设备操作 -l 指定…

08 木谷博客系统RBAC权限设计

这节内容说一下木谷博客系统的权限设计,采用现在主流的权限模型RBAC,对应关系如下: 以上5张表都在mugu_auth_server这个库中 该部分的服务单独定义在user-boot这个模块中。 将角色、权限对应关系加载到Redis 木谷博客系统在认证中心颁发令牌的时候是将用户的角色保存到令牌…

LabVIEW通过编程将图形类控件的X轴显示为时间戳

LabVIEW通过编程将图形类控件的X轴显示为时间戳 每个版本的LabVIEW中都有属性节点&#xff0c;可以以编程方式调整X轴和Y轴格式。对于不同版本的LabVIEW&#xff0c;这些属性节点无法在同一个位置找到。请参阅以下部分&#xff0c;了解特定版本LabVIEW的相关属性节点的位置。 …

静态住宅IP代理实际应用:它的强大用途你知道吗?

静态住宅IP代理与动态IP代理相比&#xff0c;提供了更稳定的网络身份&#xff0c;使得企业在进行数据采集、区域定位营销和市场研究时更为高效。同时&#xff0c;它也是提高在线隐私保护和避免封禁的有效工具。 通过详细分析&#xff0c;你将能全面了解静态住宅IP代理的应用&a…

C语言题目强化-DAY12

题型指引 一、选择题二、编程题 ★★写在前面★★ 本题库源自互联网&#xff0c;仅作为个人学习使用&#xff0c;记录C语言题目练习的过程&#xff0c;如果对你也有帮助&#xff0c;那就点个赞吧。 一、选择题 1、请阅读以下程序&#xff0c;其运行结果是&#xff08; &#x…

IDEA不支持Java8了怎么办?

IDEA不支持Java8了怎么办&#xff1f; 01 异常发生场景 当我准备创建一个springboot项目时&#xff0c;发现Java8没了 02 问题的产生及其原因 查阅了官方文档之后&#xff0c;确认了是Spring Boot 不再支持 Java 8&#xff0c;不是我的问题&#xff0c;这一天终于还是来了 0…

互联网架构演变过程梳理和架构思想的学习

文章目录 版权声明业务架构单体模式中台战略去中台化 数据架构单数据库架构主从读写分库分表高速缓存数据多样化分布式文件nosql搜索引擎架构特点 应用架构单机调优动静分离SOA微服务 部署架构单机部署⻆⾊划分应⽤集群多层代理异地访问云平台 架构思想总结 版权声明 本博客的…

数据结构与算法之美学习笔记:27 | 递归树:如何借助树来求解递归算法的时间复杂度?

目录 前言递归树与时间复杂度分析实战一&#xff1a;分析快速排序的时间复杂度实战二&#xff1a;分析斐波那契数列的时间复杂度实战三&#xff1a;分析全排列的时间复杂度内容小结 前言 本节课程思维导图&#xff1a; 今天&#xff0c;我们来讲这种数据结构的一种特殊应用&am…

springcloud进销存管理系统源码

开发说明&#xff1a; jdk1.8&#xff0c;mysql5.7&#xff0c;idea&#xff0c;vscode springcloud springboot mybatis vue elementui 功能介绍&#xff1a; 后台管理&#xff1a; 统计分析&#xff1a;查看产品&#xff0c;采购&#xff0c;销售数量&#xff1b;统计近…

51单片机制作数字频率计

文章目录 简介设计思路工作原理Proteus软件仿真软件程序实验现象测量误差和范围总结 简介 数字频率计是能实现对周期性变化信号频率测量的仪器。传统的频率计通常是用很多的逻辑电路和时序电路来实现的&#xff0c;这种电路一般运行较慢&#xff0c;而且测量频率的范围较小。这…

webpack 使用打包报错 ERROR in node_modules\@types\node\ts4.8\assert.d.ts

报错如下&#xff1a; 解决方式&#xff0c;先查看自己的 node 版本 node -v然后再安装 types/node 对应版本&#xff0c;比如我的如下 npm i types/node14.10.0 -D然后再次打包&#xff0c;就没有报错了

大数据平台/大数据技术与原理-实验报告--部署ZooKeeper集群和实战ZooKeeper

实验名称 部署ZooKeeper集群和实战ZooKeeper 实验性质 &#xff08;必修、选修&#xff09; 必修 实验类型&#xff08;验证、设计、创新、综合&#xff09; 综合 实验课时 2 实验日期 2023.11.04-2023.11.05 实验仪器设备以及实验软硬件要求 专业实验室&#xff08…

sed命令

目录 一、sed 1.sed命令选项 2.语法选项 3.sed脚本格式 4.搜索替代 5.分组后向引用 1.提取版本号&#xff1a; 2.提取IP地址 3.提取数字权限 6.变量 二、免交互 1.多行重定向 2.免交互脚本 总结&#xff1a;本章主要介绍了seq和免交互的用法及相关知识 一、sed s…