线程池的讲解和实现

文章介绍了线程池的概念,强调其能降低资源消耗和提高响应速度,并通过银行场景解释了为何从线程池取线程更高效。文章还讨论了Java中的ExecutorService和ThreadPoolExecutor,包括核心线程数、最大线程数、拒绝策略,并给出了自定义线程池的实现示例。
摘要由CSDN通过智能技术生成

🚀🚀🚀🚀🚀🚀🚀大家好,今天为大家带来线程池相关知识的讲解,并且实现一个线程池

                                    

🌸🌸🌸🌸🌸🌸🌸🌸

                                         目录

                                  🌸 1.线程池的定义

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

                         🌸3.线程池的拒绝策略

                       🌸4.线程池的实现

                  🌸5.线程池的执行流程

1.线程池的定义

我们可以这样来理解

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

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

  1. 降低资源消耗:减少线程的创建和销毁带来的性能开销。
  2. 提高响应速度:当任务来时可以直接使用,不用等待线程创建
  3. 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢占系统资源导致的阻塞现象

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

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

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

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

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

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

 场景:银行

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

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

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

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

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

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

这真是一次愉悦的体验


 场景:银行

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

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

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

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

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

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

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

这真的不是愉悦的体验

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

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

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

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()方法

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

下面我们来说一说ThreadpoolExecutor类

    • int corePoolSize:核心线程数
    • int maximumPoolSize:最大线程数=核心线程数+临时线程数
    • long keepAliveTime:临时线程保持存活的时间

      • TimeUnit unit:时间单位  s,分钟,ms

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

3.线程池的拒绝策略

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

    • ThreadPoolExecutor.AbortPolicy

      被拒绝的任务的处理程序,抛出一个 RejectedExecutionException

      //也就是说当队列满了,再添加任务,就抛出异常,原来的任务和新增的都不干了

      • ThreadPoolExecutor.CallerRunsPolicy

        一个被拒绝的任务的处理程序,直接在 execute方法的调用线程中运行被拒绝的任务,除非执行程序已经被关闭,否则这个任务被丢弃。

        //拒绝执行新来的任务,让发布这个任务或者调用这个任务的人自己去执行,添加的线程去执行这个任务

        • ThreadPoolExecutor.DiscardOldestPolicy

          被拒绝的任务的处理程序,丢弃最旧的未处理请求,然后重试 execute ,除非执行程序关闭,在这种情况下,任务被丢弃。

          //丢弃最老任务,执行新来的任务,最老任务是指最下安排的任务,也就是阻塞队列的队首元素

上述的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!!!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值