多线程---线程池

文章目录

什么是线程池?

线程诞生的原因就是进程太"重量"了。虽然线程的速度已经比进程快了很多,但是如果遇到频繁的创建和销毁线程的情况,那么线程创建和销毁的开销仍不可忽略。这时候就可以使用线程池来进一步提高使用线程的效率。

线程池就是一个"池子"里有很多线程。当需要执行任务的时候,不需要重新创建线程而是直接从"池子"里面取一个线程直接使用;用完了也不需要销毁,再放回"池子"里就行。

为什么从"池子"里取一个线程比创建一个线程快呢?

  1. 从"池子"里取的操作是一个纯用户态的操作;而创建一个线程涉及到了用户态到内核态的转化。很容易理解,转化走的路程远自然执行速度就慢。
  2. 用户态:每个进程自己执行自己的逻辑;内核态:一个操作系统只有一个内核,一个内核要给所有的进程提供服务。
  3. 线程本质是一个PCB,是内核中的数据结构。创建和销毁就要到内核中。

线程池的实现

标准库中的线程池(四种)

java 复制代码
     //标准库的线程池
    public static void main(String[] args) {
        //创建线程数目动态增长的线程池
        ExecutorService pool = Executors.newCachedThreadPool();
        //创建固定线程数的线程池
        ExecutorService pool1 = Executors.newFixedThreadPool(5);
        //创建只包含单个线程的线程池
        ExecutorService pool2 = Executors.newSingleThreadExecutor();
        //创建定期执行命令的线程池
        ExecutorService pool3 = Executors.newScheduledThreadPool(5);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程池");
            }
        });
    }

自己实现一个线程池

一个线程池可以同时提交N个任务,有M个线程同时执行这些任务。

使用生产者消费者模型来解决这个问题。

java 复制代码
class MyThreadPool{
    private BlockingQueue<Runnable> blockingQueue = new LinkedBlockingDeque<>();

    public void submit(Runnable runnable){
        try {
            blockingQueue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public MyThreadPool(int m){
        for (int i = 0; i < m; i++){
            Thread thread = new Thread(() -> {
                while (true){
                    try {
                        Runnable runnable = blockingQueue.take();
                        runnable.run();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });

            thread.start();
        }
    }
}

线程池支持的参数

corePoolSize:核心线程数

maximumPoolSize:最大线程数 最大线程数 = 核心线程数 + 临时线程数

keepAliveTime:允许临时线程空闲的时间

unit:时间的单位

workQueue:手动给线程池传一个任务队列

threadFactory:描述了线程是如何创建的。 工厂对象就负责创建线程,程序员可以手动指定创建线程的策略

handler:线程池的拒绝策略。即:线程池的任务队列已经满了,但是还有人往进添加新的任务,该怎么办?

解释:

策略一:不光新添加的任务不干,旧的任务也不干

策略二:让传递任务的线程干,如果它不干就把这个任务舍弃

策略三:不干新添加的任务,继续执行旧的任务

策略四:干新添加的任务,把旧任务中最早添加进来的任务舍弃

在实际的开发中,线程池的线程数如何确定?

盖棺定论:线程数是不能确定的,需要根据具体的情况具体分析。

因为:

  1. 主机的CPU配置不确定。CPU越多可创建的线程数越多。
  2. 不同程序的执行特点不确定。得看你的代码是干啥的:
    • 如果是100%CPU密集型任务(进行大量的算术运算和逻辑判断),线程数有最大值(比如N)。线程数就算再多也没用,因为CPU已经被占满了。
    • 如果是10%CPU密集型、90%IO密集型任务(进行大量的读写硬盘/网卡操作),线程数可以为10N

但是在工作中,两种任务各占比多少是不确定的。因此,需要我们进行实验验证:

针对自己的程序进行性能测试,分别给线程池设置不同的线程数,比如0.2N、0.5N、N、1.5N、2N.分别记录每种情况下你的程序的一些核心性能指标和负载情况,选择一个适合的线程数即可。

相关推荐
程序员黑豆3 分钟前
Java中的null与NullPointerException完全指南:安全处理、实战排查与面试题
java·前端·ai编程
晴天1625 分钟前
Electron面试题-Day19
java·javascript·electron
从小就看凹凸曼^o^1 小时前
Python基础5 - 字典与集合:(1)字典
开发语言·python
GeekZHR1 小时前
C语言指针进阶补充6:动态内存管理、mem系列内存函数、复杂指针声明,一次补齐指针的“三大盲区“
java·c语言·算法·指针
侧耳倾听1111 小时前
java 日志框架简介
java·开发语言
zlinear数据采集卡2 小时前
数据采集卡从入门到精通(9):分辨率与精度——16位卡不等于1/65536的精度
开发语言·数据库·fpga开发·开源·c#
她的男孩2 小时前
我用 LLM 把后台 CRUD 效率提升 10 倍:AI 代码生成器的架构与落地实践
java·后端·架构
全栈技术负责人2 小时前
解密 MCP(Model Context Protocol):大模型时代的“Type-C”总线与三驾马车架构深度解析
开发语言·ai·架构
丰锋ff2 小时前
1.7在Qt窗口中添加右键菜单
开发语言·qt
Titan20242 小时前
Linux网络学习:套接字、UDP的封装与应用
linux·服务器·开发语言·网络·c++