【Java】多线程池的工作原理与实践

目录

一、什么是线程池

二、线程池常用类和接口

三、线程池常见方法

1. 执行线程任务(execute、submit)

2. 关闭线程池(shutdown、shutdownNow)

四、线程池的执行流程

五、线程池的配置参数

五、线程池的分类

六、线程池的状态

七、线程池分类总结

1. FixedThreadPool

2. CachedThreadPool

3. SingleThreadExecutor

4.ScheduledThreadPool


一、什么是线程池

        线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。

二、线程池常用类和接口

        在 Java 标准库提供了如下几个类或接口,来操作并使用线程池:

        (1)ExecutorService 接口:进行线程池的操作访问

        (2)Executors 类:创建线程池的工具类

// 固定数目的线程池
ExecutorService exector = Executors.newFixedThreadPool(3) 
     
// 动态数目的线程池(线程被缓存,提供重复使用效率)
ExecutorService exector = Executors.newCachedThreadPool()      

// 仅包含1个线程的线程池(将大量的线程任务保存至工作队列,然后按照提交顺序,用一条线程依次处理)
ExecutorService exector = Executors.newSingleThreadExecutor()   

// 可以按照时间进行调度执行任务的线程池
ExecutorService exector = ​​​​​​​Executors.newScheduledThreadPool(5)   

        (3)ThreadPoolExecutor 及其子类:封装线程池的核心参数和运行机制

        // 手动创建线程池
        ExecutorService executorService = new ThreadPoolExecutor(
                5, 20, 60, TimeUnit.SECONDS,  // 核心线程数为5,最大线程数为20,非核心线程存活时间为60s
                new LinkedBlockingQueue<Runnable>()  // 工作队列,无界(容量)
                                                     // ArrayBlockingQueue<Runnable>(30) 有界,容量为30  
        );

三、线程池常见方法

  • 执行无返回值的线程任务:void execute(Runnable command)
  • 提交有返回值的线程任务:Future<T>submit(Callable<T>task)
  • 关闭线程池:void shutdown()或 shutdownNow()
  • 等待线程池关闭:boolean awaitTermination(long timeout,TimeUnit unit)

1. 执行线程任务(executesubmit)

        execute()只能提交 Runnable类型的任务,没有返回值,而submit()既能提交 Runnable 类型任务也能提交Callable 类型任务,可以返回 Future 类型结果,用于获取线程任务执行结果。

        execute()方法提交的任务异常是直接抛出的,而submit()方法是捕获异常,当调用 Future 的 get()方法获取返回值时,才会抛出异常。     

2. 关闭线程池(shutdownshutdownNow

        线程池在程序结束的时候要关闭。使用shutdown()方法关闭线程池的时候,它会等待正在执行的任务先完成,然后再关闭shutdownNow()会立刻停止正在执行的任务;

        当使用 awaitTermination()方法时,主线程会处于一种等待的状态,按照指定的 timeout 检查线程池。              
        第一个参数指定的是时间第二个参数指定的是时间单位(当前是秒)。返回值类型为 boolean 型
        如果等待的时间超过指定的时间,但是线程池中的线程运行完毕,awaitTermination()返回 true
        如果等待的时间超过指定的时间,但是线程池中的线程未运行完毕,waitTermination()返false
        如果等待时间没有超过指定时间,则继续等待。

四、线程池的执行流程

1. 提交一个新线程任务,线程池会在线程池中分配一个空闲线程,用于执行线程任务;

2. 如果线程池中不存在空闲线程,则线程池会判断当前“存活的线程数”是否小于核心线程数corePoolsize。

        2.1. 如果小于核心线程数corePoolsize,线程池会创建一个新线程(核心线程)去处理新线程任务;
        2.2. 如果大于核心线程数 corePoolsize,线程池会检查工作队列
        
                2.2.1. 如果工作队列未满,则将该线程任务放入工作队列进行等待。线程池中如果出现空闲线程,将从工作队列中按照FIFO的规则取出1个线程任务并分配执行;
                2.2.2. 如果工作队列已满,则判断线程数是否达到最大线程数maximumPoolSize。

                        2.2.2.1. 如果当前“存活线程数”没有达到最大线程数 maximumPoolsize,则创建一个新线程(非核心线程)执行新线程任务;
                        2.2.2.2. 如果当前“存活线程数”已经达到最大线程数 maximumPoolSize直接采用拒绝策略处理新线程任务。
                       
综上所述,执行顺序为:核心线程、工作队列、非核心线程、拒绝策略

五、线程池的配置参数

1. corePoolsize 线程池核心线程数:也可以理解为线程池维护的最小线程数量,核心线程创建后不会被回收。大于核心线程数的线程,在空闲时间超过 keepAliveTime 后会被回收;
        在创建了线程池后,默认情况下,线程池中并没有任何线程,当调用 execute()方法添加一个任务时,如果正在运行的线程数量小于 corePoolsize,则马上创建新线程并运行这个任务。

2. maximumPoolsize 线程池最大线程数线程池允许创建的最大线程数量;(包含核心线程池数量)。

3. keepAliveTime 非核心线程线程存活时间:当一个可被回收的线程的空闲时间大于 keepAliveTime ,就会被回收。
        当线程池中的线程数大于 corePoolsize 时,如果一个线程空闲的时间达到 keepAliveTime ,则会被回收,直到线程池中的线程数不超过 corePoolsize;
        如果设置 allowCoreThreadTime0ut =true,在线程池中的线程数不大于 corePoolsize 时,keepAliveTime 参数也会起作用,直到线程池中的线程数为0。

4. TimeUnit 时间单位:参数 keepAliveTime 的时间单位。

5. BlockingQueue 阻塞工作队列:用来存储等待执行的任务。

6. ThreadFactory 线程工厂:用于创建线程,以及自定义线程名称,需要实现ThreadFactory 接口。

7. RejectedExecutionHandler 拒绝策略:当线程池线程内的线程耗尽,并日工作队列达到已满时,新提交的任务将使用拒绝策略进行处理;
        ThreadPoolExecutor.AbortPolicy :默认策略,丢弃任务并抛出 RejectedExecutionException 异常;
        ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常;
        ThreadPoolExecutor.Discard0ldestPolicy:丢弃工作队列中的队头任务(即最旧的任务,也就是最早进入队列的任务)后,继续将当前任务提交给线程池;
        ThreadPoolExecutor.callerRunsPolicy:由原调用线程处理该任务(谁调用,谁处理);
        自定义策略:实现RejectedExecutionHandler接口。

五、线程池的分类

        Java 标准库提供的几种常用线程池,创建这些线程池的方法都被封装
到 Executors 具类中。

  • FixedThreadPool:线程数固定的线程池,使用 Executors.newFixedThreadPool()创建;
  • CachedThreadPool:线程数根据任务动态调整的线程池,使Executors.newCachedThreadPool()创建;
  • SingleThreadExecutor:仅提供一个单线程的线程池,使用Executors.newSingleThreadExecutor()创建;
  • ScheduledThreadPool:能实现定时、周期性任务的线程池,使用Executors.newScheduledThreadPool()创建;

六、线程池的状态

        线程池的状态分为:RUNNINGSHUTDOWNSTOPTIDYINGTERMINATED

        RUNNING运行状态,线程池被一旦被创建,就处于 RUNNING 状态,并且线程池中的任务数为0。该状态的线程池会接收新任务,并处理工作队列中的任务。
                调用线程池的 shutdown()方法,可以切换到SHUTDOWN关闭状态;
                调用线程池的 shutdownNow()方法,可以切换到 STOP停止状态;
       
        SHUTDOWN关闭状态,该状态的线程池不会接收新任务,但会处理工作队列中的任务;
                当工作队列为空时,并且线程池中执行的任务也为空时,线程池进入 TIDYING 状态;

        STOP停止状态,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行 的任务;
                线程池中执行的任务为空,进入 TIDYING 状态:
       
        TIDYING整理状态,该状态表明所有的任务已经运行终止,记录的任务数量为0;
                terminated()执行完毕,进入TERMINATED 状态;

        TERMINATED终止状态,该状态表示线程池彻底关闭。

七、线程池分类总结

1. FixedThreadPool

        线程数固定的线程池
        线程池参数:
                a. 核心线程数和最大线程数一致
                b. 非核心线程线程空闲存活时间,即 keepAliveTime为0
                c. 阻塞队列为无界队列 LinkedBlockingQueue
        工作机制:
                a. 提交线程任务
                b. 如果线程数少于核心线程,创建核心线程执行任务
                c. 如果线程数等于核心线程,把任务添加到LinkedBlockingQueue 阻塞队列
                d. 如果线程执行完任务,去阻塞队列取任务,继续执行
        使用场景:适用于处理CPU 密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。

2. CachedThreadPool

        可缓存线程池,线程数根据任务动态调整的线程池
        线程池参数:
                a. 核心线程数为 0
                b. 最大线程数为 Integer.MAX_VALUE
                c. 工作队列是 SynchronousQueue 同步队列
                d. 非核心线程空闲存活时间为 60 秒
        工作机制:
                a. 提交线程任务
                b. 因为核心线程数为 0,所以任务直接加到SynchronousQueue 工作队列
                c. 判断是否有空闲线程,如果有,就去取出任务执行
                d. 如果没有空闲线程,就新建一个线程执行
                e. 执行完任务的线程,还可以存活60 秒,如果在这期间,接到任务,可以继续存活下去;否则,被销毁。
        使用场景: 用于并发执行大量短期的小任务

3. SingleThreadExecutor

        单线程化的线程池
        线程池参数:
                a. 核心线程数为 1
                b. 最大线程数也为 1
                c. 阻塞队列是 LinkedBlockingQueue
                d. 非核心线程空闲存活时间为 0秒
        使用场景: 适用于串行执行任务的场景,将任务按顺序执行。

4.ScheduledThreadPool

        能实现定时周期性任务的线程池
        线程池参数:
                a. 最大线程数为 Integer.MAXVALUE
                b. 阻塞队列是 DelayedWorkQueue
                c. keepAliveTime为0
        使用场景:周期性执行任务,并且需要限制线程数量的需求场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值