线程池的介绍

目录

一、什么是线程池

二、线程池的详细内容

三、线程池的简化


一、什么是线程池

提到线程池,我们可能想到 常量池,可以先来说说常量池:

像是字符串常量,在Java程序最初构建的时候,就已经准备好了,等程序运行的时候,这样的常量也就加载到内存中了,省下来构造/销毁的开销

计算机中, 这个词,就只有这一个意思,表示的含义都是一样的

一个类似的例子:

海王:一个备胎不够用,我需要同时聊多个备胎,此时这多个备胎,就构成了"备胎池"。

一池子备胎,现在虽然还没用呢,随时拿出来使用,相关的池还有,

字符串常量池,线程池,进程池,内存池,数据库连接池......

线程池,就是为了让我们高效的创建销毁线程的

最大的好处就是减少每次启动,销毁线程的销毁

最初引入线程的原因:频繁创建销毁进程,太慢了

随着发展,我们对性能要求更进一步,现在觉得频繁创建销毁进程,开销有点不能接受了,解决方案有两个:

  1. 线程池

  2. 协程(纤程,轻量级线程),这个先不说

对于线程池,可以这样理解:

把线程提前创建好,放到一个地方(放到类似于数组),需要的时候,随时去取,用完还回池子中

在详细介绍线程池之前,可能会有这样的疑惑:

为什么认为,直接创建线程开销比从池子里取线程更大呢?这里大致说明一下。

一个操作系统 = 内核 + 配套的应用程序

内核包含操作系统的各种核心功能

  1. 管理硬件设备

  2. 给软件提供稳定的运行环境

一个操作系统,内核就是一份,一份内核,要给所有的应用程序技工服务支持。

  • 如果有一段代码时应用程序中自行完成的,整个代码是可控的
  • 如果有一段代码,需要进入内核中,由内核负责完成一系列工作,这个过程,不可控,程序员写的代码干预不了

从线程池取线程的线程,纯应用程序代码就可以完成【可控】

从操作系统创建新线程,就需要操作系统内核配合完成【不可控】

使用线程池,就可以省下应用程序切换到内核中运行这样的开销

因此,通常认为,可控的过程比不可控的过程更高效

二、线程池的详细内容

Java标准库里也提供了直接使用的线程池:ThreadPoolExecutor

线程池里准备好一些线程,让这些线程,让这些线程执行一些任务

核心方法,submit(Runnable)

通过Runnable描述一段要执行的任务

通过submit任务放到线程池里,此时线程池里的线程就会执行这样的任务

构造这个类的时候,构造方法,比较麻烦(参数比较多)

Java标准库中给了这四个构造方法,我们可以详细介绍第四个,因为这里包含前三个的所有参数。

  1. int corePoolsize,核心线程数,至少有多少个线程,线程池一创建,这些线程也要随之创建,直到整个线程池销毁,这些线程才会销毁

  2. int maximumPoolsize,最大线程数,核心线程+非核心线程(不繁忙就销毁,繁忙就再创建)线程也不是越多越好.

  3. Long keepAliveTime,非核心线程允许空闲的最大时间,(类似于允许实习生摸鱼的时间)

  4. TimeUnit unit,keepAliveTime的时间单位,是秒,分钟还是小时

  5. BlockingQueue<Runnable> workQueue 工作队列

    选择使用数组/链表,指定capacity,指定是否要带有优先级

    线程池,本质上也就是 生产者消费者模型

    调用 submit就是生产任务,线程池里的线程就是在消费任务

  6. ThreadFactory threadFactory,统一的构造并初始化线程

    工厂模式(也是一种设计模式,和单例模式是并列的关系),给线程类提供的工厂类,线程中有一些属性可以设置,线程池是一组线程

    用来弥补构造方法的缺陷,下面的代码会报错,因为触发了方法的重载

    java 复制代码
    class Point{
        public Point(double x, double y){
            
        }
        
        public Point(double r, double a){
            
        }
    }

    构造方法的名字是固定的,要想要提供不同的版本,就需要通过重载,有时候不一定能构成重载,这时候就需要用到工厂模式。

    工厂模式的核心,通过静态方法,把构造对象new的过程(各种属性初始化的过程)封装起来,提供多组静态方法,实现不同情况的构造。

    java 复制代码
    class Point{
        public static Point makePointByXY(double x, double y){
            Point p = new Point();
            //通过 x 和 y 给 p 进行属性设置
            return p;
        }
    
        public static Point makePointByRA(double r, double a){
            Point p = new Point();
            //通过 r 和 a 给 p 进行属性设置
            return p;
        }
    }
  7. RejectedExecutionHandlder handler,拒绝策略

    整个线程七个参数中,最重要的,最复杂的

    submit把任务添加到任务队列中,任务队列就是阻塞队列

    队列满了,一般不希望程序阻塞太多

    如果调用submit就阻塞(业务逻辑中的线程调用submit),就会使这个线程没法干别的事情了,不是一个好的选择,这个线程要响应用户的请求阻塞了,用户迟迟拿不到请求的回应,直观上看到的现象"卡了"

    但是与其"卡了"不如直接告诉我"失败了"有四种解决方案

三、线程池的简化

Java标准库中,以提供了另一组类,针对ThreadPoolExecutor进行了进一步封装,简化线程池的使用,也是基于 工厂设计模式

java 复制代码
public class demo2 {
    public static void main(String[] args) {
        //ExecutorService threadPool = Executors.newFixedThreadPool(4);
        ExecutorService threadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {
            int id = i;
            threadPool.submit(() -> {
                System.out.println("hello" + id + "," + Thread.currentThread().getName());
            });
        }
    }
}
相关推荐
异常君9 分钟前
分布式锁隐患解析:当业务执行时间超过锁过期时间的完整对策
java·redis·后端
V功夫兔11 分钟前
Spring_MVC 快速入门指南
java·笔记·spring·springmvc
掘金詹姆斯13 分钟前
在项目中如何进行分库分表?
java·mysql
旅行的狮子13 分钟前
二、在springboot 中使用 AIService
java·spring boot·langchain4j
扎瓦15 分钟前
Java 动态代理
java·后端·面试
码农小灰21 分钟前
Java 自动装箱与拆箱:基本数据类型与包装类的转换
java
Ares-Wang21 分钟前
kubernetes》》k8s》》Endpoint
java·容器·kubernetes
dme.29 分钟前
python爬虫复习
开发语言·爬虫·python
JANYI201838 分钟前
C语言中的双链表和单链表详细解释与实现
c语言·开发语言·windows
CatShitK40 分钟前
【Android】 如何将 APK 内置为系统应用(适用于编辑设置属性)
android·java·linux