线程的高效利用——线程池

news2024/11/15 11:51:42

文章目录

    • 线程的开销
    • 线程池的工作方式
    • ThreadPoolExecutor基础
    • 线程池结果的处理

线程的开销

线程作为一种昂贵的资源,开销包括如下几点:
1、线程的创建与启动的开销。
2、线程的销毁的开销。
3、线程调度的开销。线程的调度会产生上下文切换,从而增加处理器资源的消耗。
4、一个系统可以创建的线程数受限于该系统的处理器数目。线程数量的临界值总是处理器的数目。

线程池的工作方式

鉴于以上线程资源的消耗,我们需要一种有效的使用线程的方式,线程池就是高效利用线程的一种常见方式。它的工作原理如下:
线程池内部可以预先创建好一定数量的工作者线程,客户端将其需要执行的任务作为一个对象提交给线程池,线程池可以将这些任务缓存到工作队列中,而线程池内部则不断的从队列中取出任务并执行。因此,线程池可以看做是生产者消费者模式,其内部维护的工作者线程相当于消费者线程,客户端的线程相当于生产者线程,客户端提交的代码相当于产品,线程池内部的工作队列相当于信道。

ThreadPoolExecutor基础

ThreadPoolExecutor就是一个常用的线程池,它参数最多的构造器声明如下

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

corePoolSize: 它是线程池的核心线程大小,在线程池初期,来一个任务就会创建一个线程,当线程池中的线程数达到核心线程时,新来的任务就会存入到工作队列中。所以corePoolSize相当于一个临界值。
maximumPoolSize:它用来指定最大线程池大小。当线程池中工作队列满了之后,线程池就会继续创先线程,但是创建的线程数量不能超过maximumPoolSize。
keepAliveTime–unit:这两个参数一起用来指定线程中空闲线程的最大存活时间。空闲线程数即
maximumPoolSize-corePoolSize。
workQueue:工作队列,相当于生产者消费者模式中的传输通道。
threadFactory:指定用于创建工作者线程的线程工厂。
handler:线程池的拒绝策略。该方法如下:
在这里插入图片描述
r代表的是执行的任务,executor代表的是当前的线程池实例。
其中RejectedExecutionHandler 有几个默认的实现类:
1、ThreadPoolExecutor.AbortPolicy
ThreadPoolExecutor.AbortPolicy是默认的执行器,当线程池的线程数量超过了最大线程数,则会直接抛出异常。
2、ThreadPoolExecutor.DiscardPolicy
ThreadPoolExecutor.DiscardPolicy表示丢弃当前执行的任务,但是不抛出任何异常。
3、ThreadPoolExecutor.DiscardOldestPolicy
ThreadPoolExecutor.DiscardOldestPolicy将工作中最老的任务丢掉,然后从新尝试接纳被拒绝的任务。
4、ThreadPoolExecutor.CallerRunsPolicy
ThreadPoolExecutor.CallerRunsPolicy表示在客户端执行被拒绝的任务。

各个参数的整体配合流程如下:
客户端不断的给线程池提交任务,每提交一个新的任务,线程池就创建一个线程来处理该任务,当任务数量超过核心线程数大小时,新来的任务就会存入到工作队列中,然后这些核心线程会不断的从工作队列中取出任务来执行。线程池将任务存入到工作队列中使用的BlockingQueue的非阻塞方法offer,因此工作队列满并不会使提交任务的客户端暂停。当工作队列满了之后,线程池会继续创建新的线程,直到当前线程数达到最大线程数大小,如果此时再有新的任务,客户端执行的任务就会被拒绝。那些超出线程池核心大小的线程如果没有任务的时间达到了keepAliveTime,就会被清理掉,如果keepAliveTime设置的值太小,可能导致线程池持续的创建销毁线程,反而增加了开销。

ThreadPoolExecutor.shutdown()表示用来关闭线程池,已提交到队列中的任务会继续执行,但新提交的任务则会被拒绝。
ThreadPoolExecutor.shutdownNow()关闭线程池时,正在执行的任务也会停止,新的任务也会不会执行。

线程池结果的处理

public Future<?> submit(Runnable task) 方法的参数是Runnable 接口,其抽象方法run() 没有返回值,其作用是向线程池中提交任务,并不关心返回值,且无法抛出异常。
public Future submit(Callable task)可以处理任务的返回结果,其参数是一个Callable接口,其泛型是任务返回的类型,该方法可以抛出异常。结果可以通过Future.get()来获取,当处理的任务发生异常时,Future.get()也会将异常抛出:
在这里插入图片描述

当get方法被调用时,如果该任务还未完成,则会阻塞代码。所以应该尽可能的向线程池提交任务,并且get的调用应该放到需要数据的时刻,这期间先执行其他任务。
举一个使用Fucture接口的例子,我们从网上买完东西之后不会一直等待着收货,这期间肯定会去做别的事情,以此举例写一个小demo

public class ExecutorDemo {
    final static int cpuNum = Runtime.getRuntime().availableProcessors();
    static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0,
            cpuNum * 2,
            4,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new ThreadPoolExecutor.CallerRunsPolicy());


    public static void main(String[] args) {
        List<String> list = new ArrayList<String>() {{
            add("护肤品");
            add("水果");
        }};

        List<Future<String>> futures = new CopyOnWriteArrayList<>();
        //从网上买东西,异步处理
        list.forEach(item -> {
            Future<String> stringFuture = buySomething(item);

            futures.add(stringFuture);
        });

        //dosomething else


        //东西到了,进行验货
        futures.forEach(item -> {
            //最多等待3秒
            try {
                String something = item.get(3, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }

        });

    }

    public static Future<String> buySomething(String something) {
        return threadPoolExecutor.submit(new Callable<String>() {
            @Override
            public String call() {
                return something.concat("is coming");
            }
        });
    }
}

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

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

相关文章

skywalking解析-入门

前几天从github上看最近比较火的项目&#xff0c;发现了skywalking&#xff0c;就进行了些了解&#xff0c;发现这个领域自己目前知之甚少&#xff0c;打算通过对源码的分析深入了解一下分布式追踪。首先从对skywalking介绍开始。 目录一、简介二、整体架构三、源码下载四、系统…

spark-RDD学习笔记

本文是19年学生时学习林子雨老师课程中的一些学习笔记&#xff0c;主要内容包括RDD的概念和运行原理,rdd相关编程api接口以及对应的实例。关于RDD的内容&#xff0c;这个笔记描述的2019年年底之前的pyspark版本&#xff0c;2023年年初时在pyspark的实际工作中rdd已经很少用或者…

【Kafka】Kafka概述

一、闲话 这是2023年的第一篇博客&#xff0c;祝大家在新的一年里一帆风顺&#xff0c;身体健康 二、基本要点 1、Kafka概述 Kafka是一个分布式的基于发布/订阅模式的消息队列(Message Queue)&#xff0c;主要用于大数据实时处理领域 2、消息队列的好处 解耦&#xff1a;…

CAS原理、实践、缺陷分析及优化

文章目录CAS介绍CAS在Java中的底层实现Java源码中使用举例自己实际运用乐观锁举例简单的CAS操作ABA问题及优化实践缺陷及优化只能保证单个变量操作原子性当比较失败后,通常需要不断重试,浪费CPUCAS介绍 CAS(Compare And swap),比较并交换,是一种乐观锁.它是解决多线程并行情况…

【力扣Java】第一个出现两次的字母(HashSet与<<左移)

目录 题目描述 思路与算法 方法一&#xff1a;哈希表 HashSet说明 解题代码 方法二&#xff1a;位运算 左移运算符&#xff08;<<&#xff09; 右移运算符&#xff08;>>&#xff09; 解题代码 题目描述 给你一个由小写英文字母组成的字符串 s &#xff…

事务四大特性

没有开启事务的话直接提交、修改数据&#xff0c;有事务则进行两阶段提交&#xff1a; ①原子性 undolog 来实现一旦失败就马上触发回滚 当前事务是不可再分的&#xff0c;要么一起执行&#xff0c;要么都不执行。 start transaction redolog日志池存储undolog日志&#xff…

【Android OpenCV】Visual Studio 创建支持 OpenCV 库的 CMake 工程 ④ ( OpenCV 官方代码示例 )

文章目录一、OpenCV 官方代码示例1、Windows 平台代码示例2、Android 平台代码示例参考 【Android OpenCV】Visual Studio 创建支持 OpenCV 库的 CMake 工程 ① ( 下载 OpenCV 库 | Windows 中安装 OpenCV 库 ) 博客 , 可下载 OpenCV 库 ; CSDN 下载地址 : https://download.c…

educoder数据结构 字符串匹配 第2关:实现KMP字符串匹配

本文已收录于专栏 &#x1f332;《educoder数据结构与算法_大耳朵宋宋的博客-CSDN博客》&#x1f332; 任务描述 本关的编程任务是补全 step2/kmp.cpp 文件中的KmpGenNext函数&#xff0c;以实现 KMP 字符串匹配。该函数生成给定字符串的next数组。 相关知识 第 1 关中实现…

LEETCODE 19. 删除链表的倒数第 N 个结点

给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[]示例 3&#xff1a;…

shell流程控制之条件判断练习案列

练习案例1、判断当前磁盘剩余空间是否有20G&#xff0c;如果小于20G&#xff0c;则将报警邮件发送给管理员&#xff0c;每天检查一次磁盘剩余空间。2、判断web服务是否运行&#xff08;1、查看进程的方式判断该程序是否运行&#xff0c;2、通过查看端口的方式判断该程序是否运行…

memcpy与memmove函数的区别和实现

1.函数定义 memcpy与memmove都是C语言的库函数&#xff0c;在头文件string.h中&#xff0c;作用是内存拷贝。唯一的区别是&#xff0c;当内存发生局部重叠时&#xff0c;memmove保证了拷贝的结果是正确的&#xff0c;但是memcopy不一定是正确的。但是memcpy比memmove速度快。 …

zookeeper学习笔记3(小滴课堂)

zk集群核心知识之三种角色及其作用&#xff1a; 注册中心的三种模式&#xff1a; 选举核心概念及选举状态&#xff1a; 选举发生的时机及选举算法&#xff1a; zookeeper集群的搭建&#xff1a; 我们先来启动三台虚拟机&#xff1a; 然后我们来修改一下我们的zookeeper配置&a…

Introducing Tome, AI讲演助手

随着ChatGPT进入人们的视野&#xff0c;AI开始在越来越多的领域大展拳脚&#xff0c;近期&#xff0c;一款名为Tome的讲演编辑工具&#xff08;类似幻灯片&#xff09;推出了AI辅助创作的功能&#xff0c; Tome的AI讲演功能可以更轻松地将文本、图像、音频等多种媒体元素融合到…

CPU缓存一致性协议MESI

目录 CPU高速缓存&#xff08;Cache Memory&#xff09; CPU为何要有高速缓存 带有高速缓存的CPU执行计算的流程 目前流行的多级缓存结构 多核CPU多级缓存一致性协议MESI MESI协议缓存状态 MESI状态转换 多核缓存协同操作 单核读取 双核读取 修改数据 同步数据…

“英雄无敌”(一)

注意&#xff1a;在阅读本文前建议观看对应的视频教程&#xff0c;本文主要作为那些对视频教程仍有疑问的学习者的参考资料。且本文的部分内容有些过时&#xff08;如仍使用旧版Unity的动画系统&#xff09;&#xff0c;故直接阅读本文对Unity的学习没有太大意义 视频教程&…

写小论文心得(计算机视觉领域)

简介 这篇博文打算讲一下我写小论文前后的过程&#xff0c;其中有许多地方我觉得对于新手来说还是有很多帮助的&#xff0c;包括我自己也是自己慢慢摸索出来的&#xff0c;希望能对正在准备自己第一篇论文的人有所帮助。 写论文的整个过程我想分为几个阶段&#xff1a; 1.获得…

【深度学习】生成对抗网络(GAN)生成海贼王图像 + PyTorch代码实现

文章目录一、数据集介绍1.1 lufei&#xff08;路飞&#xff09;1.2 luobin&#xff08;罗宾&#xff09;1.3 namei&#xff08;娜美&#xff09;1.4 qiaoba&#xff08;乔巴&#xff09;1.5 shanzhi&#xff08;山智&#xff09;1.6 suolong&#xff08;索隆&#xff09;1.7 wu…

【内核笔记】2022回顾-2023规划

文章目录一、2022博客总结二、2023博客规划三、改进沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 一、2022博客总结 2022年重点优化了&#x1f680;RK3399平台开发入门到精通系列专栏&#xff0c;订阅量突破2100个订阅2022年浏览量破190万2…

《梁启超家书》笔记三——交友取益,或读书取益,也要方面稍多,才有接谈交换,或开卷引进的机会

目录 1、学科选择建议 2、学习 3、所学不可太专向&#xff08;不知道学科外其他方向的了&#xff09; 4、建议欧游实地开开眼界 5、建议结婚前分开两地住 6、毕业后工作看法(分析利弊) 7、处事态度与人生观 1、学科选择建议 我很想你以生物学为主科&#xff0c;因为它是…

Vue(五)

1. 绑定class和style样式 dom里自己拿到节点&#xff0c;改它上面的class样式 shift()移除第一个class属性 push想加哪个class属性都行 59行div知道自己只能用两个样式&#xff0c;四种情况 上面这种不合法&#xff0c;就算把样式加进div也得 样式对象&#xff1a;里面的key绿…