【Java多线程】线程池探究

线程池是一种管理线程的工具,避免了线程创建和销毁的开销,提高响应速度和系统稳定性。Java中的ThreadPoolExecutor是线程池的核心类,它有核心线程数、最大线程数等参数,以及任务队列和拒绝策略。线程池的创建和任务调度由execute方法完成,阿里巴巴不建议直接使用Executors,因为它可能导致内存溢出。线程池的拒绝策略包括AbortPolicy、CallerRunsPolicy等,可以根据需求自定义。
摘要由CSDN通过智能技术生成

什么是线程池

线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。

线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

本文学习记录的线程池是JDK中提供的ThreadPoolExecutor类。

那些地方用到线程池

实际开发中项目中,禁止自己new线程。必须使用线程池来维护和创建线程。

线程池的作用

核心:复用机制。提前创建好固定的线程一直在运行状态,实现复用,限制线程创建数量。

使用线程池可以带来一系列好处:

  • 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  • 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
  • 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

线程池的创建

在这里插入图片描述

例子:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Threadpool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int finalI = i;
            executorService.execute((new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+","+finalI);
                }
            }));
        }
    }
}

//输出:
pool-1-thread-2,1
pool-1-thread-6,5
pool-1-thread-5,4
pool-1-thread-4,3
pool-1-thread-3,2
pool-1-thread-8,7
pool-1-thread-1,0
pool-1-thread-7,6
pool-1-thread-9,8
pool-1-thread-10,9
//上面程序发现没有复用,查看源码发现可以创建MAX_VALUE的线程,不能体现特点。
//改用Executors.newFixedThreadPool(),可以解决
ThreadPoolExecutor核心参数有哪些

corePoolSize:核心线程数量 一直保持运行的线程

​ 线程池中的核心线程数,默认情况下核心线程一直存活在线程池中,如果将ThreadPoolExecutor 的allowCoreThreadTimeOut 属性设为true,如果线程池一直闲置并超过了keepAliveTime 所指定的时间,核心线程就会被终止。

maximumPoolSize:最大线程数,线程池允许创建的最大线程数

keepAliveTime:超出corePoolSize后创建的线程的存活时间。

unit:keepAliveTime时间单位

workQueue:任务队列,用于保存待执行的任务。

threadFactory:线程池内部创建线程所用的工厂。

handler:任务无法执行时的处理器。

线程池底层原理

在这里插入图片描述

线程池任务调度

在这里插入图片描述

所有任务的调度都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状态、运行线程数、运行策略,决定接下来执行的流程,是直接申请线程执行,或是缓冲到队列中执行,亦或是直接拒绝该任务。其执行过程如下:

  1. 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
  2. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
  3. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  4. 如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  5. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
线程池创建的线程会一直在运行状态吗

不会。例如:配置核心线程数corePoolSize为2、最大线程数maximumPoolSize为5,我们可以通过配置超出corePoolSize核心线程数后创建的线程存活时间例如60s。在60s内没有核心线程一直没有任务执行,则会停止该线程。

为什么阿里巴巴不建议使用Executors

因为默认的Executors线程池底层是基于ThreadPoolExecutor构造函数封装的,采用无界队列存放缓存任务,会一直缓存任务容易发生内存溢出,会导致我们最大线程数失效。

ThreadPoolExecutor底层实现原理
手写线程池
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

public class MyExecutors {
    private List<WorkThread> workThreads;
    private BlockingDeque<Runnable> runnableDeque;
    private boolean isRun = true;

    /*
    最大线程数
    @param maxThreadCount
     */
    public MyExecutors(int maxThreadCount, int dequeSize){
        //1.限制队列容量缓存
        runnableDeque = new LinkedBlockingDeque<Runnable>(dequeSize);
        //2.提前创建好固定的的线程一直在运行状态---死循环实现
        new ArrayList<WorkThread>(maxThreadCount);
        for (int i = 0; i < maxThreadCount; i++) {
            new WorkThread().start();
        }
    }
    class WorkThread extends Thread{
        public void run(){
            while (isRun || runnableDeque.size()>0){
                Runnable runnable = runnableDeque.poll();
                if (runnable != null){
                    runnable.run();
                }
            }
        }
    }
    public boolean execute(Runnable command){
        return runnableDeque.offer(command);
    }

    public static void main(String[] args) {
        MyExecutors myExecutors = new MyExecutors(2,2);
        for (int i = 0; i < 10; i++) {
            final int finalI = i;
            myExecutors.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"," + finalI);
                }
            });
        }
        myExecutors.isRun = false;
    }
}

实际最多执行多少个任务 核心线程数+缓存队列的容量+最大线程数-核心线程数

线程池拒绝策略类型有哪些

1.AbortPolicy 丢弃任务,抛运行时异常

2.CallerRunsPolicy 执行任务

3.DiscardPolicy 忽视,什么都不会发生

4.DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务

5.实现 RejectedExecutionHandler 接口,可自定义处理器

线程池状态和生命周期

在这里插入图片描述

参考资料

全面学习可参考:

Java线程池实现原理及其在美团业务中的实践

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值