JAVAEE-8-线程池

news2024/11/29 2:49:34

我们之前也接触过,比如说常量池,数据库连接池,线程池,进程池,内存池等等,

池的共性:

1.提前把要用的对象准备好

2.把用完的对象也不要立即释放,先留着以备下次使用

来提高效率!!!

最开始,进程能够解决并发编程的问题,因为频繁创建销毁进程的开销成本太大了,所以我们引进了轻量级进程===>线程.

如果销毁线程的频率进一步增大,那么此时线程的创建销毁开销成本,就不能再被无视了.

优化线程的创建销毁效率:

解决方案:

1.引入轻量级线程==>也称为纤程/协程

协程本质,是程序员在用户态代码进行调度,不是靠内核的调度器调度的,节省了很多调度上的开销.

协程是在用户代码中,基于线程封装出来的.有可能是n个协程对应着一个线程,也有可能是n个协程对应着M个线程.

2.线程池:

把要使用的线程提前创建好,用完了也不要直接释放而是以备下次使用,就节省了创建/销毁线程的开销.在这个使用的过程,并没有真的频繁创建销毁,而只是从线程池里,取线程使用,用完了放回线程池里而已.

从线程池里取代码,纯用户态代码,是可控的.

通过系统申请创建线程,就需要内核来完成的.(不太可控).

标准库中的线程池:
ThreadPoolExecutor

int corePoolSize 核心线程数:一个线程池里最少得有多少个线程

/**标准库中提供的线程数量并非是一成不变的.而是会根据当前任务量,自适应线程个数,任务多了,线程就会多几个,任务少了,线程就会少几个

int maximumPoolSize 最大线程数,一个线程池里,最多能有多少个线程

keepAliveTime是保持存活时间,成为实习生线程,就是允许最大的空闲摸鱼时间,如果当前的这个实习生线程,空闲超过了这个时间阈值,就会被销毁掉,

unit 单元变量,s/min/hour/ms

Runnable,作为描述任务的主体,和定时器类似,线程池中也可以有多个任务,也可以设置PriorityBlockingQueue,带有优先级

线程工厂,-->工厂模式,也是一种常见的设计模式.通过专门的"工厂类"/"工厂对象"来创建制定的对象

在这个类里面提供了方法(也不一定非得是静态的)

让方法封装了new操作,并且同时给Thread设置了一些属性,才构成了ThreadFactory线程工厂

工厂模式解析

//举例,表示平面上的一个点

class Point{

public Point(double x,double y);//通过笛卡尔积来初始化

public Point(double r,double a)';//通过极坐标来初始化

因为上述代码无法通过编译,所以我们设计工厂模式来解决上述问题}

class Point{

public static PointMakeByXY(double x,double y){

Point p=new Point();

p.setX(x);

p.setY(y);

}

public static PointMakeByra(double r,double a){

Point p=new Point();

p.setR(r);

p.setA(a);

}

Point p1=Point.PointMakeByxy(x,y);

Point p2=Point.PointMakeByra(r,a);

//工厂模式的核心思路,使用一些类或者对象来使用静态方法对new操作进行封装,然后再去单独的去设置他的一些属性,在方法内部设定不同的属性来完成对象的初始化,构造对象的过程.来达到我们想要的结果.

拒绝策略;

在线程池里,有一个阻塞队列,能够容纳的元素有上限,当任务队列已经满了的时候,如果继续往队列中添加任务,线程池会怎么办?

第一个,AbortPolicy:如果继续添加任务,他会直接抛出异常RejectedExecutionException,导致新的任务无法完成,旧的任务也无法完成了.

第二个:CallerRunsPolicy,新的任务,由添加任务的线程来负责执行,就是调用这个方法的线程去完成,不是线程池完成

第三个:DiscardOldesPolicy,丢弃最老的任务,新增加的任务会由线程池执行

第四个:DiscardPolicy,会把最新的任务丢弃掉,谁也不去执行,调用的线程不执行,线程池也不去执行.

ThreadPoolExecutor本身比较复杂,因此标准库提供了另外一个版本,把ThreadPoolExecutor进行了封装,封装成Executors,工厂类.通过这个类来创建出不同的线程池对象(在内部把ThreadPoolExecutor创建好了并且设置了不同的參数)

ExecutorService service=Executors.newFixedThreadPool(4);
        service.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });

我们在实际开发中,创建线程池的时候,很多时候都需要去设定线程池的线程数量.

不同的程序,能够设定的线程的数量是不同的,必须要具体问题具体分析.

我们要判断一个线程是CPU密集型的任务还是IO密集型的任务.

对于CPU密集型的任务(这个线程大部分时间都要在CPU上运行,进行计算)

对于IO密集型任务(这个线程大部分的时间都在等待IO,不需要去CPU上运行),比如线程run,scanner,读取用户的输入.

如果一个进程中,所有的线程都是CPU密集型的,每个线程所有的工作都是CPU上执行的.(极端情况)

此时,线程数目就不该超过N(cpu逻辑核心数)

如果一个进程中,所有的线程都是IO密集型的,每个线程的大部分工作都是在等待IO,cpu消耗非常少,此时的线程数目就可以很多,远远超过N.

上述两个情况都是极端情况,在实际开发中,大概率是一部分是IO一部分是CPU的.

这里的比例,需要我们通过具体的实验测试出来.

综上,由于程序的复杂性,很难直接对于线程池的线程数目进行估算.

更适合的做法,通过实验/测试的方式来找到合适的线程数目.

尝试给线程池,设定不同的线程数目,分别进行性能测试.

衡量每种线程数目下,总的时间开销,和系统资源占用的开销,找到这两者之间合适的值.

自定义线程池创建

class MyThreadPoolExecutor{//自定义线程池
    private ArrayList<Thread>threadArrayList=new ArrayList<>();
    //用来存储后续设定的线程,方便后续处理
    private BlockingQueue<Runnable> queue=new ArrayBlockingQueue<>(4);
    //用来存储执行任务的队列
    public MyThreadPoolExecutor(int n){//n为线程池创建出来的线程数目
        for(int i=0;i<n;i++){
            Thread t=new Thread(()->{
                while(true){

                    try {
                        Runnable runnable=queue.take();//取出需要执行的任务,这里的队列使用take,是带有阻塞功能的
                        runnable.run();//取出来之后进行使用
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }
            });
            threadArrayList.add(t);
            t.start();
        }

    }
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);//这里进行放入任务
    }


}

测试代码

public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPoolExecutor executor=new MyThreadPoolExecutor(4);
        for(int i=0;i<1000;i++){
            int n=i;//因为变量捕获
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行任务"+n+"线程为"+Thread.currentThread().getName());
                }
            });
        }
    }
}

运行结果 

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

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

相关文章

接口自动化测试框架的搭建

经过了一年多的接口测试工作&#xff0c;旧的框架也做了一些新的调整&#xff0c;删除了很多冗余的功能&#xff0c;只保留了最基本的接口结构验证、接口回归测试、线上定时巡检功能。 框架的演进 1.界面 UI 做了优化&#xff0c;整个框架的画风突然不一样了&#xff08;人靠…

11、虚函数、多态、纯虚函数

11、虚函数、多态、纯虚函数 虚函数覆盖调用 多态实现多态的两个必要条件多态 和 this指针多态的实现&#xff1a;虚函数表虚函数表与动态绑定动态绑定动态绑定对性能的影响 纯虚函数抽象类纯抽象类 虚函数 形如class 类名{ virtual 返回值 函数名(形参表) { … } }; 的成员函…

Git merge 与 Git rebase 与 Git fetch

Git merge 与 Git rebase 看这个图就行了 git merge、git rebase 和 git fetch 是 Git 中的三个不同的命令&#xff0c;它们分别用于不同的目的。以下是它们的主要区别&#xff1a; git merge&#xff08;合并&#xff09;&#xff1a; 用途&#xff1a; 用于将一个分支的更改…

pta模拟题——7-34 刮刮彩票

“刮刮彩票”是一款网络游戏里面的一个小游戏。如图所示&#xff1a; 每次游戏玩家会拿到一张彩票&#xff0c;上面会有 9 个数字&#xff0c;分别为数字 1 到数字 9&#xff0c;数字各不重复&#xff0c;并以 33 的“九宫格”形式排布在彩票上。 在游戏开始时能看见一个位置上…

带你搞懂JavaScript中的原型和原型链

简介 原型和原型链是JavaScript中与对象有关的重要概念&#xff0c;但是部分前端开发者却不太理解&#xff0c;也不清楚原型链有什么用处。其实&#xff0c;学过其他面对对象语言的同学应该了解&#xff0c;对象是由类生成的实例&#xff0c;类与类之间有继承的关系。在ES6之前…

html网页设计 01marquee标签广告滚动(1)

<!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><!-- scrollamount:数字越大&#xff0c;滚动越快direction:滚动方向滚动的类型behaior"slide",文字滚动到边界后就会…

Redis分布式缓存超详细总结!

文章目录 前言一、Redis持久化解决数据丢失问题1.RDB&#xff08;Redis Database Backup file&#xff09;持久化&#xff08;1&#xff09;执行RDB&#xff08;2&#xff09;RDB方式bgsave的基本流程&#xff08;3&#xff09;RDB会在什么时候执行&#xff1f;save 60 1000代表…

Vulnhub-DC-9 靶机复现完整过程

一、搭建环境 kali的IP地址是&#xff1a;192.168.200.14 DC-9的IP地址暂时未知 二、信息收集 1、探索同网段下存活的主机 arp-scan -l #2、探索开放的端口 开启端口有&#xff1a;80和22端口 3、目录扫描 访问80 端口显示的主页面 分别点击其他几个页面 可以看到是用户…

13、C++异常处理

13、c异常处理 抛出异常捕获异常未抛出异常时的流程抛出异常时的流程捕获异常匹配顺序异常说明异常处理构造函数中的异常析构函数中的异常标准库异常类 抛出异常 throw 异常对象可以抛出基本类型的对象&#xff0c;如:throw -1;throw "内存分配失败!";也可以抛出类类…

【C语言:动态内存管理】

文章目录 前言1.malloc2.free3.calloc4.realloc5.动态内存常见错误6.动态内存经典笔试题分析7.柔性数组8.C/C中的内存区域划分 前言 文章的标题是动态内存管理&#xff0c;那什么是动态内存管理&#xff1f;为什么有动态内存管理呢&#xff1f; 回顾一下以前学的知识&#xff…

C++ Core Guidelines解析 ( 好书推荐 )

C Core Guidelines是Bjarne和 Herb Sutter发起编写的一个开源项目&#xff0c;汇聚了 C社区多年来积累的宝贵经验&#xff0c;是非常全面的编程最佳实践指导&#xff0c;包括代码风格、函数、类、错误处理、性能优化等&#xff0c;可以说是C社区的集大成者。用Effective Modern…

电脑主板支持的cpu型号汇总

一、如何选择不同的主板和对应CPU 1、看针脚&#xff1a;网上有相应的参数&#xff0c;只要CPU能安装到主板中&#xff0c;基本就兼容&#xff0c;这主要取决CPU插槽和主板插槽十分一致。 2、看型号&#xff1a;桌面处理器&#xff0c;只有Intel和AMD两大平台&#xff0c;他们对…

dToF直方图之美_deadtime死区时间

上节在激光雷达多目标测距中有个问题为什么激光雷达不用做pile up算法,有人会有疑问,我看过很多人的简历,都把pile up量产校正算法写为最为自豪重要的算法攻坚,可能会吸引一波人的眼球。这要是在两三年前是值得被肯定的,但是如今随着dToF非常多量产项目落地,pile up研究不…

【Hive】——安装部署

1 MetaData&#xff08;元数据&#xff09; 2 MetaStore &#xff08;元数据服务&#xff09; 3 MetaStore配置方式 3.1 内嵌模式 3.2 本地模式 3.3 远程模式 4 安装前准备 <!-- 整合hive --><property><name>hadoop.proxyuser.root.hosts</name><v…

BUUCTF-[GYCTF2020]FlaskApp flask爆破pin

这道题不需要爆破也可以getshell ssti都给你了 {{((lipsum.__globals__.__builtins__[__import__](so[::-1])[popen]("\x63\x61\x74\x20\x2f\x74\x68\x69\x73\x5f\x69\x73\x5f\x74\x68\x65\x5f\x66\x6c\x61\x67\x2e\x74\x78\x74")).read())}} 但是学习记录一下pin…

ETLCloud详解,如何实现最佳实践及问题排查

ETLCloud介绍 ETLCloud是新一代全域数据集成平台&#xff0c;领先于市场同类产品的数据集成平台(DataOps)&#xff0c;只需单击几下即可完成数据清洗转换、传输入仓等操作&#xff0c;具备高效、智能、一站式的全域数据集成优势&#xff0c;如&#xff1a; 毫秒级实时数据同步 …

MMLM之Gemini:《Introducing Gemini: our largest and most capable AI model》的翻译与解读

MMLM之Gemini&#xff1a;《Introducing Gemini: our largest and most capable AI model》的翻译与解读 导读&#xff1a;2023年12月6日&#xff0c;Google重磅发布大规模多模态模型Gemini&#xff0c;表示了Google语言模型发展到了一个新阶段&#xff0c;其多模态和通用能力明…

力扣1445 连续字符

目录 ​编辑 题目 示例 示例1 示例2 提示 详细解读 题目 给你一个字符串 s &#xff0c;字符串的「能量」定义为&#xff1a;只包含一种字符的最长非空子字符串的长度。 请你返回字符串 s 的 能量。 解题思路 这个问题的解法相对比较简单&#xff0c;可以通过遍历字…

int(1) 和 int(10) 的区别

int(1) 和 int(10) 的区别 最近遇到个问题&#xff0c;有个表的要加个user_id字段&#xff0c;user_id字段可能很大&#xff0c;于是我提mysql工单alter table xxx ADD user_id int(1)。领导看到我的sql工单&#xff0c;于是说&#xff1a;这int(1)怕是不够用吧&#xff0c;接…

Python finally-资源回收

有些时候&#xff0c;程序在 try 块里打开了一些物理资源&#xff08;例如数据库连接、网络连接和磁盘文件等&#xff09;&#xff0c;这些物理资源都必须被显式回收。 Python 的垃圾回收机制不会回收任何物理资源&#xff0c;只能回收内存中对象所占用的内存。 那么在哪里回收…