线程池的基础小结

线程池概述

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

相关的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) 取到任务就执行,如果任务取空,线程就被阻塞,直到有新任务

相关推荐
TeDi TIVE33 分钟前
springboot和springframework版本依赖关系
java·spring boot·后端
雨辰AI33 分钟前
SpringBoot3 + 人大金仓 V9 微服务监控实战|Prometheus+Grafana+SkyWalking 全链路监控
数据库·后端·微服务·grafana·prometheus·skywalking
Nicander1 小时前
理解 mybatis 源码:vibe-coding一个mini-mybatis
后端·mybatis
小呆呆6662 小时前
Codex 穷鬼大救星
前端·人工智能·后端
FelixBitSoul2 小时前
缓存淘汰策略全解:从原理到手写实现(Java / Go / Python)
后端·面试
AI人工智能+电脑小能手3 小时前
【大白话说Java面试题】【Java基础篇】第29题:静态代理和动态代理的区别是什么
java·开发语言·后端·面试·代理模式
dovens4 小时前
SpringBoot集成MQTT客户端
java·spring boot·后端
❀͜͡傀儡师4 小时前
Spring Boot 集成 RocksDB 实战:打造高性能 KV 存储加速层
java·spring boot·后端·rocksdb
TeamDev4 小时前
如何在 DotNetBrowser 中使用本地 AI 模型
前端·后端·.net