线程池(面试常考)

🍊一. 认识线程池

关于"池"的概念,我们接触过字符串常量池,数据库连接池,它们都被用作共享和缓存资源,通俗的将就是使用的时候直接从池子里拿,线程池也一样,在初始化的时候,就创建一定数量元素,后面需要使用线程就直接从线程池中取
我们之前使用多线程的时候都会创建线程和销毁线程,但是创建和销毁都会耗费大量资源,所以线程池的作用就是减小创建和销毁时的损耗

🍬举例形象说明线程池:

我们假设线程池为一个快递公司,里面有正式员工,但是在双11,双12因为快递量大所以有临时工,但是高峰期过后需要解雇临时工,快递都放在仓库,当仓库满了之后就不在接收快递,快递员送快递是从仓库拿快递然后送

🍉二. 原生线程池(ThreadPoolExecutor)

ThreadPoolExecutor提供了更多的参数,可以进一步细化线程池的行为

🍬ThreadPoolExecutor的构造方法:

🍬构造方法参数解析:对应上述例子来结合理解

🍃corePoolSize,核心线程数:正式员工

🍃maximumPoolSize,最大线程数:正式员工和临时员工总数

🍃keepAliveTime,空闲时间:临时工空闲(keepAliveTime,TimeUnit结合来决定何时解雇临时工)

🍃TimeUnit,空闲时间单位:空闲时间(keepAliveTime,TimeUnit结合来决定何时解雇临时工)

🍃workQueue,阻塞队列:快递仓库

🍃threadFactory:使用工厂对象提供的方法来创建线程(了解)

🍃RejectedExecutionHandler,拒绝策略:仓库满了不再接收快递

🍬拒绝策略:

🍂AbortPolicy(): 以抛出异常的方式拒绝(默认的拒绝策略)
🍂CallerRunsPolicy(): 让调用的线程来处理
🍂DiscardOldestPolicy(): 丢弃时间最久的任务(先进先出)
🍂DiscardPolicy(): 丢弃新来的任务

👁‍🗨️实现代码:

java 复制代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
 
//使用原生api来创建(ThreadPoolExecutor)
public class ThreadPool {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3, //线程核心数
                10, //最大线程数
                60,  //空闲时间
                TimeUnit.SECONDS, //空闲时间单位
                new ArrayBlockingQueue<>(100), //阻塞队列
                new ThreadPoolExecutor.AbortPolicy()  //默认拒绝策略,抛出异常
//                new ThreadPoolExecutor.CallerRunsPolicy() 让调用线程自己处理
//                new ThreadPoolExecutor.DiscardOldestPolicy() 丢弃时间最久任务
//                new ThreadPoolExecutor.DiscardPolicy() 丢弃新来的任务
        );
        //提交任务使用:submit/execute
        for(int i = 0;i < 10;i++){
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

🍏三. ExecutorService和 Executors创建线程池

这种创建方式对应就是ThreadPoolExecutor构造参数中的threadFactory参数,使用工厂对象提供的创建方式

ExecutorService 表示一个线程池实例

Executors 是一个工厂类, 能够创建出几种不同风格的线程池
提交任务到线程池中的阻塞队列中:
🍁execute(Runnable task)
🍁submit(Runnable task)
🍁submit(Callable task)

🍬Executors创建线程池的几种方式:

☘️newFixedThreadPool:创建固定线程数的线程池

☘️newCachedThreadPool:创建线程数目动态增长的线程池

☘️newSingleThreadExecutor:创建只包含单个线程的线程池

☘️newScheduledThreadPool:创建有计划任务的线程池(带有定时器功能)

👁‍🗨️示例代码:

java 复制代码
        //创建有缓存的线程池
        ExecutorService pool1 = Executors.newCachedThreadPool();
        //创建有固定大小的线程池
        ExecutorService pool2 = Executors.newFixedThreadPool(4);
        //创建有计划任务的线程池
        ExecutorService pool3 = Executors.newScheduledThreadPool(4);
        //创建只有单个线程的线程池
        ExecutorService pool4 = Executors.newSingleThreadExecutor();

👁‍🗨️注意:这种创建的方式在以后的工作中不建议用

🍂在工作中要阻塞队列设置大小,如果不设置大小,在某个时间会导致内存不够,出现OOM(内存溢出)

🍂拒绝策略要自己扩展实现(比如任务记录在日志或者数据库里)

🫐四. 线程池的工作流程

结合上述快递公司例子说明:

🍋五. 线程池的模拟实现

前提说明:

🍁这里阻塞队列的实现采用链表的方式阻塞

🍁我们要求线程池创建的时候,就创建线程不停的从队列中取任务来执行

🍁这里创建5个员工

👁‍🗨️代码实现:

java 复制代码
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
 
public class MyThreadPool {
    private BlockingDeque<Runnable> queue = new LinkedBlockingDeque<>();
 
    //员工的数量
    public MyThreadPool(int num){
        for(int i = 0;i < num;i++){
            //线程池创建的时候,就创建线程不停的从队列中取任务来执行
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while(true){ //不停的取任务
                        try {
                            Runnable task = queue.take(); //获取任务
                            task.run(); //执行任务
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }
 
    //放任务
    public void execute(Runnable task){
        try {
            queue.put(task);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(5);
        while(true){
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
            Thread.sleep(1000);
        }
    }
}

部分打印结果:

相关推荐
计算机毕业设计小帅1 天前
【2026计算机毕业设计】基于Springboot的校园电动车短租平台
spring boot·后端·课程设计
调试人生的显微镜1 天前
Web前端开发工具实战指南 从开发到调试的完整提效方案
后端
静心观复1 天前
drawio画java的uml的类图时,class和interface的区别是什么
java·uml·draw.io
Java水解1 天前
【SQL】MySQL中空值处理COALESCE函数
后端·mysql
Laplaces Demon1 天前
Spring 源码学习(十四)—— HandlerMethodArgumentResolver
java·开发语言·学习
guygg881 天前
Java 无锁方式实现高性能线程
java·开发语言
ss2731 天前
手写Spring第7弹:Spring IoC容器深度解析:XML配置的完整指南
java·前端·数据库
Python私教1 天前
DRF:Django REST Framework框架介绍
后端·python·django
间彧1 天前
Java HashMap如何合理指定初始容量
后端
用户4099322502121 天前
PostgreSQL全表扫描慢到崩溃?建索引+改查询+更统计信息三招能破?
后端·ai编程·trae