线程池的讲解和实现

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

   目录

                                 🌸 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!!!

相关文章
|
3月前
|
Java 调度
基于C++11的线程池
基于C++11的线程池
|
5月前
|
存储 缓存 安全
线程池相关详解
线程池相关详解
|
缓存 Java
线程池简单总结
线程池简单总结
|
6月前
|
缓存 算法 Java
|
Java
6. 实现简单的线程池
6. 实现简单的线程池
55 0
|
缓存 Java 调度
线程池的介绍
线程池的介绍
|
存储 Java 调度
线程池使用
线程池使用
|
Java 调度
线程池 的一些事
线程池 的一些事
130 0
线程池 的一些事
|
消息中间件 监控 搜索推荐
线程池:我是谁?我在哪儿?
大家好,这篇文章跟大家探讨下日常使用线程池的各种姿势,重点介绍怎么在 Spring 环境中正确使用线程池。
305 1
线程池:我是谁?我在哪儿?
|
Java 程序员
我是一个线程池
我是一个线程池