教你一文读清线程池的优势和底层实现(JUC编程)

1.线程池简介

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了线程池的最大数量,则超出部分的线程在等候区排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
他的主要特点是:线程复用;控制最大并发数;管理线程。
他的主要优点有:

  1. 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度:当任务到达时,任务可以拿取已创建的线程直接执行。
  3. 提高线程的可管理性:线程是稀缺资源如果无限制的创建,不仅会消耗系统资源,还会降低系统稳定性,使用线程池可以进行统一的分配,调优和监控。

2.线程池框架

在这里插入图片描述
线程池底层就是ThreadPoolExcutor类,而Exexutors就是用来创建、管理线程池的工具类,类似于数组工具类Arrays。
工具类创建线程池的3个核心方法:

  1. Executors.newFixedThreadPool(int n)   : 一池n线程,执行长期的任务,性能优越
  2. Executors.newSingleThreadExecutor()  : 一池一线程,一个任务一个任务执行的场景
  3. Executors.newCachedThreadPool()       : 一池多线程可扩容,执行大批量且短期的异步任务

前三种获取线程的方式可通过右边链接回顾:
读清手动方式创建线程的优缺点

3.第四种获得java多线程的方式:线程池演示

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

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 一池3个处理线程
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        // 模拟10个待办任务,都交给线程池的execute()执行
        try {
            for (int i = 1; i < 10; i++) {
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " 执行了任务");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述

4.从Executors 线程池工具类3种创建线程的核心方法,获知底层都是实例化了ThreadPoolExecutor类,只是构造参数不同。

4.1. Executors.newFixedThreadPool(int n):

主要特点为:- 创建定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
                      - newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的是LinkedBlockingQueue阻塞队列。
2. Executors.newFixedThreadPool(int n)

4.2.Executors.newSingleThreadExecutor():

主要特点为:- 创建一个单线程的线程池,它只有一个工作线程执行任务,保证所有任务按照指定顺序执行。    - newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,它使用的是LinkeBlockingQueue阻塞队列。
在这里插入图片描述

4.3.Executors.newCachedThreadPool():

主要特点为:- 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
                      - newCachedThreadPool将corePoolSize设置为0,maximumPoolSize设置为最大Int值,它使用的是SynchronousQueue同步队列。所有线程执行待办任务,如果某个线程空闲超过60秒,则销毁该线程。
在这里插入图片描述

5.new ThreadPoolExecutor(…)线程池7大重要参数介绍

所有线程池底层的构造方法都是下面这个方法:
在这里插入图片描述
图解如下:

1.corePoolSize:线程池中的常驻核心线程数。
2.maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值必须大于等于1。
3.keepAliveTime:多余的空闲线程的存活时间,当前线程池数量超过maximumPoolSize时,空闲时间达到keepAliveTime值的多余线程将会被销毁,直至剩下corePoolSize个线程为止。
4.unit:keepAliveTime的时间单位。
5.workQueue:任务队列,存放已提交但尚未被执行的任务。
6.threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般使用工厂的默认参数即可。
7.handler:拒绝策略,表示当任务队列满了,并且工作线程>=线程池的最大线程数(maximumPoolSize)时如何来拒绝新增的任务

7.线程池的工作原理

在这里插入图片描述
工作原理图解如下:

1.在创建了线程池后,等待提交过来的任务请求。
2.当调用executor()方法添加一个请求任务时,线程池会做如下判断:
    2.1.如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行待办任务;
    2.2.如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列
    2.3.如果这时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
    2.4.如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
3.当一个线程完成任务时,它会从队列中取出下一个任务来执行。
4.当一个线程无事可做超过一定时间(keepAliveTime)时,线程池会进行判断逻辑:如果当前在运行的线程数大于corePoolSize,那么这个空闲线程就是多余的线程,会被销毁掉。

8.JDK的4种线程池内置拒绝策略

拒绝现象发生的时机:阻塞队列满了,再也塞不下新的任务。同时线程池中的线程数已经达到了max,无法再次创建线程。
4种内置策略及其解释如下:

1.AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行。
2.CallerRunsPolicy:“调用者运行”是一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,由调用者线程执行该任务。
3.DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。
4.DiscardPolicy:直接丢弃任务,不允任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。

JDK默认拒绝策略如下:
在这里插入图片描述

9.生产环境中使用的线程池

在生产环境中Executors的3种直接创建线程池的方法都不宜使用,而是使用手写构造参数来自定义线程池。
理由如 阿里开发手册 所言:
在这里插入图片描述

9.1自定义线程池参数演示

在这里插入图片描述
由演示代码可知当前线程池的最大线程数为2,阻塞队列空间为2,并发待执行任务为10。从构造参数和并发数可知会导致线程池拒绝策略的执行,线程池的拒绝策略使用的是CallerRunsPolicy,即当并发任务数导致队列满,并大于max线程数时,之后到达的任务将会由main线程代为执行,结果如下:
在这里插入图片描述

9.2合理配置线程池的考虑

1.调用如下代码查看服务器的CPU核数
在这里插入图片描述
2.区分待执行任务的类型

  • CPU密集型:是指该任务需要大量的运算,而没有阻塞,CPU一直全速运行。
                          此时应该配置尽可能少的线程数量:CPU核数+1个线程的线程池。
  • IO密集型:
                   1.IO密集型的任务线程并不是一直在执行任务,则应配置尽可能多的线程:CPU核数*2
                   2.该任务需要大量的IO,即大量的阻塞,故需要多配置线程数:CPU核数 / (1 - 阻塞系数)
                      比如8核CPU:8 / (1 - 0.9) = 80个线程数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值