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{
    @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 创建的简单线程池可能足够使用,但需要谨慎考虑其限制和适用性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python数据预处理详解

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 数据预处理是数据科学中至关重要的步骤&#xff0c;它包括清洗、转换、归一化等操作&#xff0c;以使数据适合于机器学习模型的使用。Python提供了多种强大的库和工具&#xff0c;能够帮助进行数据预处理。本文将…

C语言枚举的作用是什么?

我在知乎上看到这个问题&#xff0c;一开始&#xff0c;也有一些疑惑&#xff0c;后面查了一些资料&#xff0c;对于这个问题&#xff0c;简单的说一下我的看法。 枚举有多大 枚举类型到底有多大&#xff0c;占多少空间呢&#xff1f;这个要具体情况具体分析&#xff0c;编译器…

第八节HarmonyOS @Component自定义组件的生命周期

在开始之前&#xff0c;我们先明确自定义组件和页面的关系&#xff1a; 1、自定义组件&#xff1a;Component装饰的UI单元&#xff0c;可以组合多个系统组件实现UI的复用。 2、页面&#xff1a;即应用的UI页面。可以由一个或者多个自定义组件组成&#xff0c;Entry装饰的自定…

LeetCode Hot100 108.将有序数组转为二叉搜索树

题目&#xff1a; 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 方法&#xff1a; class Solution {public…

深信服防火墙设置应用控制策略(菜鸟必看)

PS&#xff1a;前几天发布了关于深信服防火墙路由部署的流程&#xff1a;深信服防火墙路由模式开局部署-手把手教学&#xff08;小白篇&#xff09;-CSDN博客 昨天晚上有csdn的朋友联系我&#xff0c;说有一个关于ACL访问的问题要帮忙看一下 解决了以后&#xff0c;写个大概的…

Week-T11-优化器对比试验

文章目录 一、准备环境二、准备数据三、搭建训练网络三、训练模型&#xff08;1&#xff09;VSCode训练情况&#xff1a;&#xff08;2&#xff09;jupyter notebook训练情况&#xff1a; 四、模型评估 & 模型预测1、绘制Accuracy-Loss图2、显示model2的预测效果 五、总结1…

C++ 泛型编程,函数模版和类模版

1.泛型编程 泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础 就比如说活字印刷术&#xff0c;就是提供一个模具&#xff0c;然后根据模具来印刷出不同的字。 泛型编程跟着类似&#xff0c;提供一个模版&#xff0c;根据这…

五、Microsoft群集服务(MSCS)环境的搭建

一、【目的】 学会利用Windows Server布置群集环境。 二、【设备】 FreeNAS11.2&#xff0c;Windows Server 2019 三、【要求】 学会利用Windows Server布置群集环境&#xff0c;掌握处理问题的能力。 配置表&#xff1a; 节点公网IP(public)内网IP(private)群集IP(clust…

欧拉LINUX 23.09版本上安装ORACLE 19c

前面解决了在RHEL9上安装ORACLE 19C的问题后&#xff0c;发现龙蜥 LINUX23 上可以安装ORACLE19C,网上搜了一下&#xff0c;欧拉 linux 22.03 上&#xff0c;没有成功安装ORACLE 19c 的先例&#xff0c;23.09就更不用说了&#xff0c;但看到的错误&#xff0c;不外服都是缺 libp…

玻色量子真机测试完整报告

​ 真机测试 2023年 2023.8 量子计算突破云渲染资源调度&#xff01;真机测试完整报告公开&#xff01; 2023.8 量子计算突破金融信用评分&#xff01;真机测试完整报告公开&#xff01; 组合优化问题专题 2023年 2023.7 玻色量子“揭秘”之旅行商问题与Ising建模 2023.…

OPENWRT解决配置pppoe后无法光猫路由管理界面

一、新建一个wan口 二、设置流量转发 设置完成后保存应用即可

pdf加密文件解密(pdf文件解密小工具)

工具放在文章末尾&#xff01; 1.pdf文件加密后会有很多使用权限的限制很不方便&#xff0c;只要是为了pdf的数据不被二次利用&#xff0c;未加密的pdf功能都是可以正常使用的 2.加密后的pdf使用权限会被限制部分 3.工具只能解决pdf编辑等加密情况&#xff0c;不能解决文件打…

耗时一个星期整理的APP自动化测试工具大全

在本篇文章中&#xff0c;将给大家推荐14款日常工作中经常用到的测试开发工具神器&#xff0c;涵盖了自动化测试、APP性能测试、稳定性测试、抓包工具等。 一、UI自动化测试工具 1. uiautomator2 openatx开源的ui自动化工具&#xff0c;支持Android和iOS。主要面向的编程语言…

神器!使用 patchworklib 库进行多图排版真棒啊

如果想把多个图合并放在一个图里&#xff0c;如图&#xff0c;该如何实现 好在R语言 和 Python 都有对应的解决方案&#xff0c; 分别是patchwork包和patchworklib库。 推介1 我们打造了《100个超强算法模型》&#xff0c;特点&#xff1a;从0到1轻松学习&#xff0c;原理、…

与 PCIe 相比,CXL为何低延迟高带宽?

文章目录 前言1. LatencyPCIE 生产者消费则模型结论Flit 包PCIE/CXL.ioCXL.cace & .mem总结 2. BandWidth常见开销CXL.IO Link efficiencyPCIe Link efficiencyCXL.IO bandwidthCXL.mem/.cache bandwidth 参考 前言 CXL 规范里没有具体描述与PCIe 相比低延时高带宽的原因&…

只会在终端使用Python运行代码?这些高级用法了解了解

大部分同学在终端使用Python可能只是简单的执行代码&#xff0c;但其实结合一些Python内置模块或第三方库可以实现更高级且便捷的用法&#xff0c;一起看看吧 插播&#xff0c;更多文字总结指南实用工具科技前沿动态第一时间更新在公粽号【啥都会一点的研究生】 代码Benchmar…

Linux下基于MPI的hello程序设计

Linux下基于MPI的hello程序设计 一、MPICH并行计算库安装实验环境部署创建SSH信任连接&#xff0c;实现免密钥互相连接node1安装MPICH 3.4配置NFS注意(一定要先看)环境测试 二、HELLO WORLD并行程序设计 一、MPICH并行计算库安装 在Linux环境下安装MPICH执行环境&#xff0c;配…

详细介绍如何使用深度学习自动车牌(ALPR)识别-含(数据集+源码下载)

深度学习一直是现代世界发展最快的技术之一。深度学习已经成为我们日常生活的一部分,从语音助手到自动驾驶汽车,它无处不在。其中一种应用程序是自动车牌识别 (ALPR)。顾名思义,ALPR是一项利用人工智能和深度学习的力量自动检测和识别车辆车牌字符的技术。这篇博文将重点讨论…

11.28 知识回顾(Web框架、路由控制、视图层)

一、 web 框架 1.1 web框架是什么&#xff1f; 别人帮咱们写了一些基础代码------》我们只需要在固定的位置写固定的代码--》就能实现一个web应用 Web框架&#xff08;Web framework&#xff09;是一种开发框架&#xff0c;用来支持动态网站、网络应用和网络服务的开发。这大多…

Windows环境下的JDK安装与环境配置

一、JDK下载 1、打开Oracle官方网站下载页 Java Downloads | Oracle 中国 2、选择Java archive页&#xff0c;在版本列表中选择需要下载的版本 3、选择系统环境对应的版本&#xff0c;点击对应的下载按钮&#xff0c;弹出技术许可勾选框 4、勾选Oracle技术许可协议 5、输入Or…