线程池的基础小结

线程池概述

作用:线程属于重要的系统资源,创建线程会消耗大量的服务器资源,线程池能够对线程资源进行回收,重复使用,以降低对服务器资源的消耗

相关的API:

线程池顶层的接口: Executor

主要方法: execute(Runnable) 启动线程执行任务,执行完回收线程

子接口:ExecutorService

主要方法: shutdown() 停止线程池,会让其中线程继续把任务做完

shutdownNow() 停止线程池,中断正在执行的线程

实现类:ThreadPoolExecutor 具体线程池的实现类

工具类:Executors 用于快速创建线程池

创建线程池的几种方法

使用Executors工具类创建线程池

  • ExecutorService newCachedThreadPool()

    获得长度不限的线程池

  • ExecutorService newFixedThreadPool(数量)

    获得长度固定的线程池

  • ExecutorService newSingleThreadExecutor()

    获得长度单一的线程池

  • ScheduledExecutorService newScheduledThreadPool(数量)

    获得可以调度的线程池

scss 复制代码
/**
 * 线程池案例
 */
public class ThreadPoolDemo {
​
    //不使用线程池,会造成大量线程资源的浪费
    public static void testNonThreadPool(){
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() +" 执行了");
            }).start();
        }
    }
​
    //长度不限的线程池
    public static void testCachedThreadPool(){
        //创建长度不限的线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            //使用线程池启动线程
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() +" 执行了");
            });
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //关闭线程池
        threadPool.shutdown();
    }
​
    //长度固定的线程池
    public static void testFixedThreadPool(){
        //创建长度不限的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            //使用线程池启动线程
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() +" 执行了");
            });
        }
        //关闭线程池
        threadPool.shutdown();
    }
​
    //单一线程的线程池
    public static void testSingleThreadPool(){
        //创建长度单一的线程池
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100; i++) {
            //使用线程池启动线程
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() +" 执行了");
            });
        }
        //关闭线程池
        threadPool.shutdown();
    }
​
    //可调度的线程池
    public static void testScheduledThreadPool(){
        //创建长度单一的线程池
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
        //执行调度任务,参数1 任务 参数2 初始延迟 参数3 周期 参数4 时间单位
        threadPool.scheduleAtFixedRate(() -> {
            System.out.println("Hello!! " + LocalDateTime.now());
        },2,1, TimeUnit.SECONDS);
        //关闭线程池
//        threadPool.shutdown();
    }
​
    public static void main(String[] args) {
//        testNonThreadPool();
//        testCachedThreadPool();
//        testFixedThreadPool();
//        testSingleThreadPool();
        testScheduledThreadPool();
    }
}

手动创建线程池

阿里规范要求必须手动创建线程池,不使用Executors

使用ThreadPoolExecutor类创建,构造方法的参数:

1) corePoolSize 核心线程数 ,线程池中初始的线程数量

2) maxPoolSize 最大线程数,线程池的极限

3) keepAliveTime 存活时间,允许线程空闲的时间

4) timeUnit 时间单位

5) workQueue 工作队列,阻塞队列用于保存执行任务Runnable

6) threadFactory 线程工厂,用于创建线程

7) rejectHandler 拒绝策略,处理暂时执行不了的任务

线程池的配置:

1) 核心线程数,要考虑当前任务情况:是计算密集型(大量使用CPU)还是IO密集型(大量进行数据IO)

计算密集性需要充分利用CPU内核,线程数量=CPU内核,获得方法:Runtime.getRuntime().availableProcessors();

IO密集型,耗时的IO操作会造成线程长时间阻塞,需要更多的线程,线程数量=CPU内核 * n (n >=2 n的大小取决于IO操作耗时情况和请求总数) 具体通过压力测试

2) 最大线程数,可以设置和核心线程数一样,就避免频繁创建线程和销毁线程的消耗

3) 存活时间,如果核心线程数和最大线程数一样,可以设置为0,如果不一样,尽量设置长一点,避免频繁创建和销毁

4) 阻塞队列可以使用LinkedBlockingQueue,频繁添加和删除任务的效率高

scss 复制代码
    //自定义的线程池
    public static void testThreadPoolExecutor(){
        //获得内核数
        int cpuNum = Runtime.getRuntime().availableProcessors() * 2;
        //手动创建线程池
        ExecutorService threadPool = new ThreadPoolExecutor(cpuNum,cpuNum,0,TimeUnit.SECONDS,
                new LinkedBlockingQueue<>());
        for (int i = 0; i < 1000; i++) {
            //使用线程池启动线程
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() +" 执行了");
            });
        }
        //关闭线程池
        threadPool.shutdown();
    }    

线程池的原理

线程池如何回收线程?在线程执行完任务后,不死亡

ThreadPoolExecutor类中有内部类Worker代表工作线程,内部包装Thread

所有工作线程保存在HashSet

swift 复制代码
/**
* Set containing all worker threads in pool. Accessed only when
* holding mainLock.
*/
private final HashSet<Worker> workers = new HashSet<Worker>();

线程池工作原理: execute方法

1) 执行execute方法传入Runnable对象

2) 判断当前线程有没有达到核心线程数

3) 没有就添加核心线程,有再判断是否达到最大线程数

4) 达到最大值就执行拒绝策略

5) 添加线程时,创建Worker工作线程,启动工作线程

6) 执行runWorker方法,会循环调用getTask()从工作队列取任务

7) 取到任务就执行,如果任务取空,线程就被阻塞,直到有新任务

相关推荐
文心快码BaiduComate32 分钟前
我用文心快码Spec 模式搓了个“pre作弊器”,妈妈再也不用担心我开会忘词了(附源码)
前端·后端·程序员
aiopencode44 分钟前
iOS 性能监控 运行时指标与系统行为的多工具协同方案
后端
E***U9451 小时前
从新手到入门:如何判断自己是否真的学会了 Spring Boot
数据库·spring boot·后端
招风的黑耳1 小时前
智慧养老项目:当SpringBoot遇到硬件,如何优雅地处理异常与状态管理?
java·spring boot·后端
回家路上绕了弯1 小时前
分布式锁原理深度解析:从理论到实践
分布式·后端
磊磊磊磊磊1 小时前
用AI做了个排版工具,分享一下如何高效省钱地用AI!
前端·后端·react.js
hgz07102 小时前
Spring Boot Starter机制
java·spring boot·后端
daxiang120922052 小时前
Spring boot服务启动报错 java.lang.StackOverflowError 原因分析
java·spring boot·后端
我家领养了个白胖胖2 小时前
极简集成大模型!Spring AI Alibaba ChatClient 快速上手指南
java·后端·ai编程
一代明君Kevin学长2 小时前
快速自定义一个带进度监控的文件资源类
java·前端·后端·python·文件上传·文件服务·文件流