【第九章】JUC之ThreadPoolExecutor线程池详解

一、线程池简介

  线程池主要是为了控制运行的线程数量,处理过程中将任务放到队列中,然后在线程创建后启动这些任务,如果线程数超过了最大数量,超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。

线程池主要特点是:线程复用、控制最大并发数(削峰操作)、管理线程。

二、如何使用线程池?

  Java中的线程池是通过Executor框架实现的,该框架中用到了ExecutorExecutorsExecutorsServiceThreadPoolExecutor类。

在这里插入图片描述
三个常用的线程池

实现方法描述
Executors.newFixedThreadPool(int)一池n个固定线程
Executors.newSingleThreadExecutor()一池单线程
Executors.newCachedThreadPool()可扩容线程
(1)Executors.newFixedThreadPool(int)代码实现
package com.lindaxia.juc.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程池案例
 * @author lindaxia
 * @date 2019/11/1 18:20
 */
public class MyThreadPoolDemo {
    public static void main(String[] args) {
        System.out.println();
        //创建一池n线程数--一个银行网点3个窗口
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        try {
            for (int i = 1; i <=6; i++) {
                threadPool.execute(
                    ()->{
                        System.out.println(Thread.currentThread().getName()+"\t号服务员提供服务");
                    }
                );
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();//关闭线程池
        }
    }
}
测试结果

在这里插入图片描述

(2)Executors.newSingleThreadExecutor()代码实现
// ExecutorService threadPool = Executors.newFixedThreadPool(3);

//一池单线程也能完成多任务
 ExecutorService threadPool = Executors.newSingleThreadExecutor();
测试结果

在这里插入图片描述

(3)Executors.newCachedThreadPool()代码实现
//一池多线程(一个银行网点3个窗口)
// ExecutorService threadPool = Executors.newFixedThreadPool(3);
//一池单线程(一个银行网点1个窗口)
//ExecutorService threadPool = Executors.newSingleThreadExecutor();
//池扩展线程(一个银行网点可扩展窗口)
 ExecutorService threadPool = Executors.newCachedThreadPool();
(1、2、3)方法底层实现源码

在这里插入图片描述

三、线程池底层工作原理(非常重要!!!)
(1)7大参数
参数描述
corePoolSize线程池中的常驻核心线程池(new不会创建,Execute执行任务的时候创建,延迟加载,常驻线程池!!!
maximumPoolSize线程池中能容纳同时执行的最大线程数,此值必须>=1;
keepAliveTime多余的空闲线程的存活时间,当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程会被销毁直到只剩下corePoolSize个线程为止
unitkeepAliveTime的单位
workQueue任务队列,被提交但尚未被执行的任务
threadFactory生成线程池中工作线程的线程工厂,用于创建线程,一般默认的就行
handler拒绝策略,当队列满了,并且工作线程>=线程池的最大线程(maximumPoolSize),拒绝请求执行runnable的策略

java.util.concurrent Class ThreadPoolExecutor 源码
在这里插入图片描述

(2)原理流程
①创建线程池后,线程池中的线程数为0【惰性加载】;
②当调用`execute()`方法添加一个请求任务时,线程池会做出如下判断:

  如果正在运行的线程数量小于corePoolSize,那么马上创建线 程运行这个任务;
  如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列
  如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
   如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。

③当一个线程完成任务时,它会从队列中取下一个任务来执行。

④当一个线程空闲、超过一定的时间(keepAliveTime)时,线程会判断:如果当前运行线程数大于corePoolSize,那么这个线程就被停掉。当线程池的所有任务完成后,它最终会收缩到corePoolSize的大小【自动调节池大小】。

四、手写线程池

  线程池不允许使用Executors创建,而是通过ThreadPoolExecutor的方式,避免资源耗尽的风险!

  FixedThreadPoolsingleThreadPool允许请求队列的长度Integer.MAX_VALUE,可能会堆积大量的请求(高并发高访问量)会导致OOM,(LinkedBlockingQueue链表有界阻塞队列的长度231 次方 - 1 = 2147483648 - 1 = 2147483647,运行程序的内存是有限的,所以提供的三个线程池不要使用,我们手写线程池!

(1)线程池的拒绝策略

  等待队列已满,再也塞不下新任务,同时,线程池中的最大线程数也达到极限,这时就无法继续为新任务服务。我们就需要拒绝策略机制合理的处理这个问题!

(2)JDK内置拒绝策略种类

以下策略都实现了RejectedExecutorHandle接口!

拒绝策略描述
AbortPolicy(默认)直接抛出RejectedExecutionException异常阻止系统正常运行
CallerRunsPolicy“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者(main线程),从而降低新任务的流量【吞吐量最大】
DiscardOldestPolicy抛弃队列中等待最久的任务,然后把当前任务加人队列中尝试再次提交当前任务【适合时效性的系统】
DiscardPolicy该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种策略
(3)手写线程池代码
package com.lindaxia.juc.demo;
import java.util.concurrent.*;
/**
 * 线程池案例
 * 拒绝策略(默认):AbortPolicy
 * @author lindaxia
 * @date 2019/11/1 18:20
 */
public class MyThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,          //核心线程数2
                5,      //最大线程数5
                3L,       //多余线程的空闲超时时间
                TimeUnit.SECONDS,       //单位秒
                new ArrayBlockingQueue<Runnable>(3),//数组有界阻塞队列3个
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy() //默认拒绝策略,超出最大线程数就会报异常
        );
        try {
            //3、5、8个任务,线程成功处理,9个任务已超出等待队列的容量就会抛异常
           
            for (int i = 1; i <= 9; i++) {
                threadPool.execute( //执行任务才会常见线程池
                        () -> {
                            System.out.println(Thread.currentThread().getName() + "\t号服务员提供服务");
                        }
                );
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();//关闭线程池
        }

    }
}

打印结果1

在这里插入图片描述

测试结果2

在这里插入图片描述

#轻松一刻:

 ☝上述分享来源个人总结,如果分享对您有帮忙,希望您积极转载;如果您有不同的见解,希望您积极留言,让我们一起探讨,您的鼓励将是我前进道路上一份助力,非常感谢!我会不定时更新相关技术动态,同时我也会不断完善自己,提升技术,希望与君同成长同进步!

☞本人博客:https://coding0110lin.blog.csdn.net/  欢迎转载,一起技术交流吧!

ThreadPoolExecutor 是 Java 中的一个线程池实现,它提供了一种管理线程的机制,可以有效地控制线程的数量,避免因为线程过多而导致系统资源的浪费和性能下降。 ThreadPoolExecutor 的主要构造函数如下: ``` public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) ``` 其中,各个参数的含义如下: - `corePoolSize`:核心线程数,即线程池中保持的最少线程数。 - `maximumPoolSize`:线程池所能容纳的最大线程数。 - `keepAliveTime`:线程池中超过 `corePoolSize` 的空闲线程能够存活的最长时间。 - `unit`:`keepAliveTime` 的时间单位。 - `workQueue`:任务队列,用于保存等待执行的任务。 - `threadFactory`:线程工厂,用于创建新线程。 - `handler`:拒绝策略,用于当任务队列满了且当前线程数已达到最大线程数时如何处理新任务。 ThreadPoolExecutor 在初始化时会创建 `corePoolSize` 个线程,并将剩余的任务添加到任务队列 `workQueue` 中。当任务队列满了时,如果当前线程数小于 `maximumPoolSize`,则会创建新的线程来执行任务;如果当前线程数已达到最大线程数,则会根据拒绝策略 `handler` 来处理新任务。 ThreadPoolExecutor 还提供了一些方法,如 `execute()`、`submit()`、`shutdown()` 等,用于提交任务、关闭线程池等操作。需要注意的是,当使用完线程池后,应该及时调用 `shutdown()` 方法来关闭线程池以释放资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值