线程池的讲解和实现

简介: 线程池的讲解和实现

   目录

                                 🌸 1.线程池的定义

                            🌸2.线程池相关类的认识

  🌸3.线程池的拒绝策略

                      🌸4.线程池的实现

                🌸5.线程池的执行流程


1.线程池的定义


我们可以这样来理解


我们之前创建线程的时候一直都是向系统申请资源,虽然说线程轻量,但是频繁的创建也会消耗资源,开校是不可忽略的.因此,我们就使用线程池,用到线程的时候直接到池子里取,用完之后还给线程池就好,也就是随用随调


那么线程池有什么好处呢?


降低资源消耗:减少线程的创建和销毁带来的性能开销。

提高响应速度:当任务来时可以直接使用,不用等待线程创建

可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象

为什么从线程池取线程要比从系统申请效率更高呢


因为从线程池取线程是纯粹的用户态操作


从系统创建线程,涉及到内核态和用户态的切换


说到这里,什么是内核态,什么是用户态台呢,我们来看一看官方的介绍


内核态:当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)。其他的属于用户态。用户程序运行在用户态,操作系统运行在内核态.(操作系统内核运行在内核态,而服务器运行在用户态)。用户态不能干扰内核态.所以CPU指令就有两种,特权指令和非特权指令.不同的状态对应不同的指令。


 我们举一个现实中的例子,假如A要去银行办一张银行卡,那么


64403664243640c29d9cab7433d671a5.png


场景:银行


滑稽老铁:你好,我想办一张银行卡


柜台人员:先生您好,请出示您的身份证复印件


滑稽老铁:啊,我忘记复印了我只带来身份证咋办


柜台人员:没关系的先生,您可以到大厅的复印机打印一张然后拿过来,或者您也可以给我我给您打印😊


滑稽老铁:哦哦,好的,那我去大厅的复印机打印


于是滑稽老铁很快打印完很快就回来了,并且很快完成了银行卡的办理


这真是一次愉悦的体验


场景:银行


滑稽老铁:你好,我想办一张银行卡


柜台人员:先生您好,请出示您的身份证复印件


滑稽老铁:啊,我忘记复印了我只带来身份证咋办


柜台人员:没关系的先生,您可以到大厅的复印机打印一张然后拿过来,或者您也可以给我我给您打印😊


滑稽老铁:哦哦,好的,那麻烦你给我打印一下


柜台人员:好的先生,稍等


就这样,过了很久,柜台工作人员回来了,然后给滑稽老铁办好了银行卡


这真的不是愉悦的体验


因为要是让滑稽老铁自己打印,那么滑稽老铁不会墨迹,会飞快的打印完,回来办上银行卡


如果交给工作人员,他可能会先接杯水,再和别人唠会嗑啥的,会很慢


这里的滑稽老铁就是用户态,柜台里的工作人员就是内核态,滑稽老铁去大厅自己打印就是相当于线程池取线程,是纯用户态的操作而将身份证给柜台人员打印相当于向系统申请资源创建线程,涉及到内核态和用户态的切换


2.线程池相关类的知识


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-03-25
 * Time: 13:42
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        ExecutorService pool= Executors.newFixedThreadPool(10);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
        pool.shutdown();//线程池的销毁
    }
        }


Java标准库实现了线程池

ExcutorService直译为执行服务,也就是线程

在这个代码中我们不是通过new一个 ExcutorService创建持,而是通过调用Excutors类的静态方法来完成构造


我们把不通过构造方法,而是通过普通的静态方法来完成对象创建和构造的过程叫做工厂模式

工厂模式其实就是拿来填构造方法的坑的

当我们算一个坐标的时候,我们可以通过读横纵坐标,也可以通过极坐标的方式

来写一个伪代码


class  Point{
public Point(double x,double y)
}
public  Point(double r,double a){
}
}
我想求该点坐标,有两种方法可以完成,但是这两个方法得构成重载才行,但现在完全不符合
那么我们可以搞两个普通方法实现
public static  Point   getPointByXY(double x,double y){
//在这里new对象 new point()
//设置属性
return 
}
public static Point getPointByRA(double r,double a){
//new 对象
//设置属性
//return
}

Executors 创建线程池的几种方式

newFixedThreadPool: 创建固定线程数的线程池

newCachedThreadPool: 创建线程数目动态增长的线程池.

newSingleThreadExecutor: 创建只包含单个线程的线程池.


线程池的销毁:pool.shutdown()方法


bc08260ed5c249bfb39920c00ec6b549.png


上述的工厂类都是对ThreadPoolExecutor 类的封装.

下面我们来说一说ThreadpoolExecutor类


1691214274322.png


submit方法就是把任务放到池子里


3.线程池的拒绝策略


现在来说一说线程池的拒绝策略(非常非常重要)

cc61b9058f4c43c1981ba8906eeb7beb.png

1691214256103.png

上述的ThreadPoolExecutor类的参数以及四个拒绝策略是重点!!!


4.线程池的实现


说到这里,我们已经基本清楚线程池,下面来自己实现一个线程池


import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-03-25
 * Time: 14:46
 */
class MyThreadPool{
    //阻塞队列存放任务
    private BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();
    //将任务放到队列
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
    //实现固定线程数的线程池
    public MyThreadPool(int n){
        for(int i=0;i<n;i++){
            Thread t=new Thread(()->{
                try {
                    while(true) {
                        Runnable runnable = queue.take();
                        runnable.run();
                    }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
            });
            t.start();
        }
        }
    }
public class ThreadingDemo2 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool  pool=new MyThreadPool(10);
        for(int i=0;i<1000;i++){
            int number=i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello"+number);
                }
            });
        }
    }
}


5.线程池的执行策略


1.提交了任务之后,首先会对线程数和核心数进行比较,如果线程数小于核心数就直接调用去执行

2.如果线程数大于核心数了,此时新任务就会添加到阻塞队列中等待执行,当然会判断队列否为空

3.如果线程池中存活的线程已经等于核心线程数了,并且阻塞队列也满了,然后就回去判断当前线程数是否达到最大线程数了,如果没达到就创建非核心线程去执行

任务

4.如果当前线程数已经达到了最大核心数时,此时再次添加任务线程池就会执行拒绝策略


这就是线程池今天全部的内容了,我们今天的讲解就到这里

下期再见,886!!!

相关文章
|
机器学习/深度学习 算法 搜索推荐
推荐算法介绍
推荐算法介绍
635 0
|
域名解析 弹性计算 云计算
【深度好文】中小企业上云,为什么做好网络架构规划很重要!
本文通过一位小微软件公司技术负责人的实际体验为始,引发了对大量小微企业上云架构实践的研究。 发现中小企业上云时,往往聚焦于业务测试和服务尽快上线,很难有精力投入在云上技术架构的规划和设计中。所以,大家云上的架构五花八门,很多架构缺乏长远规划,极可能给业务未来发展埋下隐患。 基于此,我们沉淀了一套《应用上云经典托管架构》,强调了上云架构规划对于业务的重要性,并带领大家理解了方案中的网络规划和架构设计全过程。 作为从事企业上云IT部门,或者初创事业的个人开发者们,都可以参考和了解。
|
负载均衡 关系型数据库 MySQL
MySQL 主主复制与主从复制对比
MySQL的主主复制和主从复制是两种常见的数据库复制配置方式,各有优缺点和适用场景。以下是对这两种复制方式的详细对比: ### 主从复制 (Master-Slave Replication) **特点:** 1. **单向复制**:数据从主服务器复制到一个或多个从服务器。从服务器只能从主服务器接收数据更新,不能向主服务器发送数据。 2. **读写分离**:主服务器处理写操作(INSERT、UPDATE、DELETE),从服务器处理读操作(SELECT),可以分担读负载,提高系统的整体性能。 3. **数据一致性**:数据在主服务器上是最新的,从服务器上可能会有一定的延迟。 **优点:**
804 1
|
存储 SQL 运维
Elasticsearch 查询革新:探索 Wildcard 类型的高效模糊匹配策略
Elasticsearch 查询革新:探索 Wildcard 类型的高效模糊匹配策略
|
设计模式 Kubernetes Cloud Native
Kubernetes 中 4 种容器设计模式
Kubernetes 中 4 种容器设计模式
Kubernetes 中 4 种容器设计模式
|
缓存 算法 Java
深入解析线程上下文切换的原理与优化策略
深入解析线程上下文切换的原理与优化策略
1256 0
|
前端开发
Nestjs(五)异常处理方式(异常过滤器)
Nestjs(五)异常处理方式(异常过滤器)
264 0
|
存储 缓存 算法
提升性能的利器:理解线程池的使用、工作原理和优势
在Java中,创建和销毁线程开销较大,为了避免线程过多而带来使用上的开销。 所以我们需要对线程进行统一管理及复用,这就是我们要说的线程池。
|
存储 JSON 安全
Oauth2.0 + JWT 做权限认证
做过权限认证的朋友都清楚,SpringSecurity 的功能很强大,但是我们也都知道,它配置起来也着实让人头疼。N多个配置类还有N多个需要实现的接口,总是记不住和不知道为什么会有这么多,最近在学习这方面的东西,正好能够把学习到的东西分享出来给大家参考一下。