Bootstrap

线程池详解


线程池概念

线程池用于管理线程创建和销毁,通过重用线程执行新的任务来减少创建和销毁线程的性能开销,提高了程序响应速度,提供了更好的系统资源管理。


线程池参数

corePoolSize:核心线程数,当任务提交时,如果线程数小于核心线程数,创建新线程执行任务。当线程数量小于等于核心线程数时,线程不会被销毁,除非关闭线程池。
maximumPoolSzie:最大线程数,线程池中最多有多少个线程。线程数大于等于核心线程数,且工作队列已满,会创建新的线程执行任务。
keepAliveTime:存活时间,当线程数大于核心线程数,且线程在keepAliveTime时间内未获取到新的任务,则销毁线程,直到线程数为核心线程数。
unit:keepAliveTime的单位
workQueue:工作队列,存放待执行任务
threadFactory:创建线程的工厂
handler:拒绝策略,当线程数量等于maximumPoolSize且工作队列已满时执行的策略

注意:
线程池中的线程其实没有一个属性标识一个线程是核心线程还是非核心线程,只有一个核心线程数的概念,即当线程数大于核心线程数时,线程池只需保证线程池中有核心线程数个线程,销毁哪个线程是随机的。
new一个线程池对象后线程池中的线程数量为0

拒绝策略

AbortPolicy:默认拒绝策略,丢弃任务并抛出RejectedExecutionException
DiscardPolicy:丢弃任务但并不抛出异常
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务。
CallerRunsPolicy:由调用线程处理该任务


线程池状态

RUNNING:能接受新的任务,且能处理工作队列中的任务
SHUTDOWN:不再接受新的任务,但能处理工作队列中的任务。在线程池处于RUNNING状态时,线程池调用shutdown方法后将状态设置为SHUTDOWN
STOP:不再接受新的任务,不执行工作队列中的任务,中断正在执行的任务。线程池处于RUNNING或SHUTDOWN状态时,线程池调用shutdownNow方法后将状态设置为STOP
TIDYING:线程池线程数量为0,所有任务均已终止,状态变为TIDYING
TERMINATED:终止状态,TIDYING状态调用terminated方法状态设置为TERMINATED,ThreaPoolExecutor中的默认terminated方法什么都没有做。
线程池状态切换过程:
在这里插入图片描述


线程池工作流程

1.判断线程数是否小于核心线程数,小于则新建线程执行提交的任务
2.当线程数等于核心线程数,此时提交任务,任务会被添加到阻塞队列,若阻塞队列已满,当前线程数小于最大线程数,则新建一个线程执行任务
3.若新建线程后阻塞队列仍会满,再创建新线程,直到创建的线程数量达到最大线程数
4.当线程数等于最大线程数,且工作队列已满,执行拒绝策略处理新提交的任务。
5.若当前线程数大于核心线程数小于等于最大线程数,线程在keepAliveTime后无法获取新任务,则线程池销毁线程

线程池提交任务流程
在这里插入图片描述

线程池使用

1.示例代码

250ms为向线程池中添加一个任务的时间,2000ms为执行一个任务的时间

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {
    static int n = 0;
    public static void main(String[] args) {

        //线程池的工作队列 ArrayBlockingQueue是一个有界的阻塞队列
        ArrayBlockingQueue<Runnable> runnables = new ArrayBlockingQueue<>(20);
        
        //创建一个线程池对象
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor
                (6, 20, 300
                        , TimeUnit.MILLISECONDS, runnables);

        for (int i = 0; i < 10000; i++) {
            try {
                Thread.sleep(250);// 控制生产速度 查看线程池状态
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("任务池:" + runnables.size() + " 线程数量:" + threadPoolExecutor.getPoolSize());
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);// 控制消费速度
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    n++;
                    System.out.println("hello" + (n));
                }
            });
        }


    }
}

2.输出

线程数小于核心线程数,创建线程执行任务,等于核心线程数量后提交的任务放入工作队列中
在这里插入图片描述
工作队列已满,创建一个新的线程执行任务
在这里插入图片描述
创建新线程后,工作队列仍满,再创建一个新线程执行任务
在这里插入图片描述

因为任务添加的速度为250ms一个,任务执行速度为2000ms一个。因此,2s添加8个任务,线程池中有8个线程同时执行任务,2s能执行8个任务,达到一个平衡的状态,工作队列中的数量基本趋于稳定且队列不会满,因此不会再创建新的线程了。

将线程池keepAliveTime改为30,单位改为TimeUnit.MICROSECONDS,微秒(μs),一毫秒等于1000微秒
在这里插入图片描述
线程数大于核心线程数,且经过30微秒未获取到新任务,销毁线程
在这里插入图片描述
在示例代码的基础上将最大线程数改为7
达到最大线程数7,且队列已满,执行默认拒绝策略,抛出RejectedExecutionException。
在这里插入图片描述

;