线程池知识点总结

线程池

线程池的优势

  1. 降低系统资源消耗,可以重用已存在的线程,减少了线程创建和销毁的不必要消耗;
  2. 提高系统响应速度,当有任务到达时,通过重用已存在线程,可以无需等待新线程的创建;
  3. 方便管理线程并发数量;
  4. 还有一些扩展功能,如延时定时线程池

线程池的流程

基础的使用

主要通过Executors的newXXXThreadPool();/newXXXThreadExecutor();方法来创建线程池(返回ExecutorService对象,ExecutorServcice是Java提供的用于管理线程池的类),通过对象的execute方法调用线程池里的线程执行对应的代码。

// FixedThreadPool(n)代表创建一个内部有固定n个的线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);

// SingleThreadExecutor()代表创建一个只有一个线程的线程池
ExecutorService executor = Executors.newSingleThreadExecutor();

// CachedThreadPool()代表创建可以无限扩大的线程池
ExecutorService executor = Executors.newCachedThreadPool();

newFixedThreadPool方法的源码:

在这里插入图片描述

newSingleThreadExecutor方法的源码:

newCachedThreadPool方法的源码:

在这里插入图片描述

使用的模板:

public void init() {
    // 三种自带的线程池的创建方式
    ExecutorService executor2 = Executors.newFixedThreadPool(5);
    ExecutorService executor3 = Executors.newSingleThreadExecutor();
    ExecutorService executor = Executors.newCachedThreadPool();
    
    try {
        for (int i = 0; i < 10; i++) {
            // 用execute来使用线程池中的线程
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " thread running");
            });

        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 最后需要关闭线程池
        executor.shutdown();
    }
}

以上的三个方法最终都调用了下面的这个构造方法:

在这里插入图片描述

线程池中构造方法中的七大参数的用途

  • int corePoolSize :
    • 表示线程池中常驻的核心线程数
  • int maximumPoolSize:
    • 表示线程池中能容纳的同时执行的最大线程数,此值必须>=1
  • long keepAliveTime:
    • 多余的空闲线程存活时间(线程池中线程数量超过corePoolSize,且空闲时间达到keepAliveTime,多余线程被销毁,到最后只剩下corePoolSize 的数量)
  • TimeUnit unit:
    • keepAliveTime的单位
  • BlockingQueue workQueue:
    • 任务队列,存放被提交但是还未执行的任务
  • ThreadFactory threadFactory:
    • 表示生产线程的线程工厂,一般默认即可
  • RejectedExecutionHandler handler:
    • 定义拒绝策略,表示当队列满了,且工作线程数大于等于线程池的最大线程数以后如,何拒绝请求执行的runnable

为什么要使用阻塞队列,而不是非阻塞的?

阻塞队列在任务队列中没有任务时,阻塞获取任务的线程,使线程进入wait状态,释放CPU的资源。

当队列中有任务时才会唤醒对应的线程,从队列中取出消息并执行。

线程池底层工作的流程

  1. 线程池创建,等待请求
  2. 当调用execute()方法后,添加一个请求任务开始判断:
    1. 如果当前正在运行的线程数量小于corePoolSize,那么马上调用线程来执行这个任务;
    2. 如果当前正在运行的线程数量大于等于corePoolSize,且等待队列未满,那么将这个任务加入队列
    3. 如果此时队列已经满了,且正在运行的线程数量小于maximumPoolSize,那么扩容,即开启非核心线程来运行这个任务;
    4. 如果队列已满,且运行的线程数量大于等于maximumPoolSize,那么线程池回启动饱和拒绝策略来应对
  3. 当一个线程完成任务时,从等待队列中取出下一个任务来执行;
  4. 当一个线程空闲超过keepAliveTime,线程判断:
    1. 当运行的线程数大于corePoolSize,那么此时这个空闲线程被停止,否则不处理
    2. 最后线程池所有任务完成后,最终会收缩到corePoolSize的大小。

创建线程池的规范

根据阿里发布的开发手册:

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这
样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

所以在实际开发时一般会自己创建线程池:

public static void main(String[] args) {
    ExecutorService threadPool =
            new ThreadPoolExecutor(2,									// 常驻核心线程数
                    Runtime.getRuntime().availableProcessors() + 1,		// 最大线程数
                    2,													// 空闲存活的时间
                    TimeUnit.SECONDS,									// 空闲存活时间的单位
                    new LinkedBlockingQueue<>(3),						// 等待队列,一般需要给定队列的大小
                    Executors.defaultThreadFactory(),					// 线程工厂,一般使用默认的
                    new ThreadPoolExecutor.DiscardOldestPolicy());		// 拒绝策略
    try {
        for (int i = 0; i < 9; i++) {
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " thread running... ...");
            });
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        threadPool.shutdown();
    }
}

注意点:

  1. 线程池的最大的线程数(maximumPoolSize)一般可以设置为本机的 逻辑处理器数量+1 ,而逻辑处理器的数量可以通过Runtime.getRuntime().availableProcessors()来获取;

  2. 存放等待任务的阻塞队列最好赋予初始值,如果使用默认情况,队列的长度会过大,这样拒绝策略相当于无效了,不可取;

    1. 在这里插入图片描述
  3. 线程工厂通过Executors.defaultThreadFactory()来创建默认工厂;

  4. 拒绝策略通过new ThreadPoolExecutor.xxxPolicy()来创建指定

四种拒绝策略:

均发生在当线程全部在使用且阻塞队列满时

1、AbortPolicy()

丢弃要进入的任务,并且抛出RejectedExecutionException异常。

2、CallerRunsPolicy()

交给直接调用此线程的线程来处理要进入的任务。

3、DiscardPolicy()

丢弃要进入的任务,但是不抛出异常,静默丢弃。

4、DiscardOldestPolicy()

丢弃阻塞队列中排在最前面的任务,然后重新提交被拒绝任务。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值