线程池ThreadPoolExecutor运行原理以及7个参数详解

线程池的作用其实和连接池差不多的概念,其作用就是为了完成线程的复用!

线程池最简单的创建形式是以以下的形式创建,

  • ExecutorService executorService = Executors.newFixedThreadPool(5);
    固定线程数量
  • ExecutorService executorService = Executors.newSingleThreadExecutor();
    线程数量为1
  • ExecutorService executorService = Executors.newCachedThreadPool();
    自动通过缓存获取线程,线程数量为N

工作中不可能用这种方式创建线程池,这种方式很容易会发生OOM(out of memory)内存溢出的情况,从源码中可以看出阻塞队列的长度为Integer的最大数,阿里巴巴开发手册已经规定,不可以使用Executors.new的方式来创建线程池, 代码如下

   /**
     * 工作中不可能用这种方式创建线程池,
     * 这种方式很容易会发生OOM(out of memory)内存溢出的情况,
     * 从源码中可以看出阻塞队列的长度为Integer的最大数,阿里巴巴开发手册已经规定,
     * 不可以使用Executors.new的方式来创建线程池
     */
    private static void testExecutor() {
        //线程池通过Executors工具类来获取
//      ExecutorService executorService = Executors.newFixedThreadPool(5);//固定线程数量
//      ExecutorService executorService = Executors.newSingleThreadExecutor();//线程数量为1
        ExecutorService executorService = Executors.newCachedThreadPool();//自动通过缓存获取线程,线程数量为N

        try {
            for (int i = 0; i < 10; i++) {
                executorService.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t 办理服务!");
                });
//                try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }

虽然上面说的是三种方式,其实最终只是一种三种方式的源码如下
线程池ThreadPoolExecutor运行原理以及7个参数详解_第1张图片
线程池ThreadPoolExecutor运行原理以及7个参数详解_第2张图片
线程池ThreadPoolExecutor运行原理以及7个参数详解_第3张图片

接着分析一下线程池创建的时候所需要的七个参数

线程池ThreadPoolExecutor运行原理以及7个参数详解_第4张图片
corePoolSize 核心线程数量,即使处于空闲状态,也保留在池中的线​​程数,除非设置了{@code allowCoreThreadTimeOut}
maximumPoolSize 最大线程数量,一般不会指定具体数字,可以通过代码获取当前的系统CPU核心数量来指定
keepAliveTime 空闲线程存活时间
unit 时间单位
workQueue 创建阻塞队列,一定声明长度,不然默认会是Integer.MAX_VALUE
threadFactory 一般情况都是用默认的线程工厂来创建线程
handler 当容量达到上限时的拒绝策略,拒绝策略有四种

new ThreadPoolExecutor.AbortPolicy()//默认拒绝策略,丢弃任务并抛出RejectedExecutionException异常。
new ThreadPoolExecutor.CallerRunsPolicy()//将任务回退给调用者
new ThreadPoolExecutor.DiscardPolicy()//不执行任何操作,丢弃线程
new ThreadPoolExecutor.DiscardOldestPolicy()//丢弃线程中的旧的任务

分析完七个参数,该分析线程池的运行原理了

首先创建了线程池过后,线程池等待任务
请求任务过来,判断是否当前的核心线程都被占用了,如果都被占用则放入阻塞队列中,否则就执行该线程
如果阻塞队列线程满了,就先判断当前是否是最大线程数,不是就扩容,如果已达到最大线程数,则通过拒绝策略来拒绝
若线程中的线程都空闲了,此时除了核心线程以外的其他线程将在空闲时间后缩减掉变为原来的核心线程数量
分析图示如下
线程池ThreadPoolExecutor运行原理以及7个参数详解_第5张图片
也就是说把这张图片以自己的话来分析讲出来,就可以了!

我在听课的时候所做的一个线程池创建的案例

package com.lp.concurrent;

import java.util.concurrent.*;

/**
 * 模拟银行办理业务窗口数为线程池中线程数量来进行运行
 *
 * @Date 2020/7/22 20:31
 * @Author luopeng
 */
public class ThreadPoolExecutorTest {
    public static void main(String[] args) {
        int processors = Runtime.getRuntime().availableProcessors();//获取CPU核心线程数
        System.out.println(processors);
        /**
         * 创建线程池并给定初始参数
         *
         * @param corePoolSize 核心线程数量,即使处于空闲状态,也保留在池中的线​​程数,
         *                     除非设置了{@code allowCoreThreadTimeOut}
         * @param maximumPoolSize 最大线程数量
         * @param keepAliveTime 空闲线程存活时间
         * @param unit 时间单位
         * @param workQueue 创建阻塞队列,一定声明长度,不然默认会是Integer.MAX_VALUE
         * @param threadFactory 一般情况都是用默认的线程工厂来创建线程
         * @param handler 当容量达到上限时的拒绝策略
         */
        ExecutorService executorService = new ThreadPoolExecutor(
                2,//核心线程数量
                /*processors*/5,//最大线程数量
                2,//空闲线程存活时间
                TimeUnit.SECONDS,//时间单位
                new LinkedBlockingQueue<>(3),//创建阻塞队列,一定声明长度,不然默认会是Integer.MAX_VALUE
                Executors.defaultThreadFactory(),//一般情况都是用默认的线程工厂来创建线程
//                new ThreadPoolExecutor.AbortPolicy()//默认拒绝策略,丢弃任务并抛出RejectedExecutionException异常。
//                new ThreadPoolExecutor.CallerRunsPolicy()//将任务回退给调用者
//                new ThreadPoolExecutor.DiscardPolicy()//不执行任何操作,丢弃线程
                new ThreadPoolExecutor.DiscardOldestPolicy()//丢弃线程中的旧的任务
        );
        try {
            for (int i = 0; i < 10; i++) {
                executorService.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t 办理服务!");
                });
//                try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }

    /**
     * 工作中不可能用这种方式创建线程池,
     * 这种方式很容易会发生OOM(out of memory)内存溢出的情况,
     * 从源码中可以看出阻塞队列的长度为Integer的最大数,阿里巴巴开发手册已经规定,
     * 不可以使用Executors.new的方式来创建线程池
     */
    private static void testExecutor() {
        //线程池通过Executors工具类来获取
//        ExecutorService executorService = Executors.newFixedThreadPool(5);//固定线程数量
//        ExecutorService executorService = Executors.newSingleThreadExecutor();//线程数量为1
        ExecutorService executorService = Executors.newCachedThreadPool();//自动通过缓存获取线程,线程数量为N

        try {
            for (int i = 0; i < 10; i++) {
                executorService.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t 办理服务!");
                });
//                try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }

}

你可能感兴趣的:(JUC,Java面试题)