ThreadPool 线程池的使用

  • Post author:
  • Post category:其他


一、什么是线程池

一种

线程

使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。(摘抄自百度百科

什么是线程池

二、线程池的优点

1、降低资源消耗;通过重复利用已创建的线程降低创建和销毁造成的消耗。

2、提高响应速度;任务到达时,不用等待线程创建便能立刻执行。

3、提高线程的可管理性;统一监控、管理、调优。

三、四种线程池的创建方式

1、newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

    public static void main(String[] args) {

        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            cachedThreadPool.execute(() -> {
                System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
            });
        }
    }

结果打印:

Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-5
Thread.currentThread().getName() = pool-1-thread-3
Thread.currentThread().getName() = pool-1-thread-4
Thread.currentThread().getName() = pool-1-thread-2
Thread.currentThread().getName() = pool-1-thread-9
Thread.currentThread().getName() = pool-1-thread-8
Thread.currentThread().getName() = pool-1-thread-7
Thread.currentThread().getName() = pool-1-thread-6
Thread.currentThread().getName() = pool-1-thread-10

2、newSingleThreadExecutor

创建一个单线程化的线程池,保证所有任务按照指定的顺序执行(FIFO,LIFO,优先级),当要求进程限制时,可以进行使用。

    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            singleThreadExecutor.execute(() -> {
                    System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
            });
        }

结果打印:

Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1

3、newFixedThreadPool

创建一个固定线程数量,可重用的线程池。

    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 10; i++) {
            final int index = i;
            fixedThreadPool.execute(() -> {
                    System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
            });
        }
    }


结果打印:

Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-2
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-2
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-1
Thread.currentThread().getName() = pool-1-thread-2

4、newScheduledThreadPool

创建一个可定期或者延时执行任务的线程池。

    public static void main(String[] args) {

        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("延迟5秒后执行");
            }
        },5, TimeUnit.SECONDS);
    


        scheduledThreadPool.scheduleAtFixedRate(() -> {
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            System.out.println("延迟3秒后,每5秒钟执行一次,当前时间:" + LocalDateTime.now().format(dateTimeFormatter));
        },3,5,TimeUnit.SECONDS);
    }

结果打印:

延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:21:58
延迟5秒后执行
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:03
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:08
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:13
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:18
延迟3秒后,每5秒钟执行一次,当前时间:2022-09-15 15:22:23
……

四、手动创建线程池

根据《阿里巴巴Java开发手册》中提到

所以我们在项目开发中,通过ThreadPoolExecutor 来手动创建线程池。那如何手动创建?

1、创建线程池工厂类

@Slf4j
public class ThreadPoolUtil {

    /** 核心线程池默认大小 */
    private static final Integer DEFAULT_CORE_POOL_SIZE = 8;
    /** 默认最大线程数 */
    private static final Integer DEFAULT_MAX_POOL_SIZE = 64;
    /** 默认空闲线程存活时间(s) */
    private static final Integer DEFAULT_FREE_THREAD_KEEP_ALIVE = 60;
    /** 默认任务队列大小 */
    private static final Integer DEFAULT_TASK_QUEUE_SIZE = 2000;
    /** 默认的线程池饱和时任务拒绝策略 */
    private static final RejectedExecutionHandler DEFAULT_REJECTED_HANDLER = new ThreadPoolExecutor.CallerRunsPolicy();

    /** 线程池对象 */
    private ThreadPoolExecutor EXECUTOR;
    /** 工厂实例 */
    private static volatile ThreadPoolUtil INSTANCE;
    /** 线程名称前缀 **/
    private static final String THREAD_POOL_PRE_NAME = "XX业务_pool";


    private ThreadPoolUtil(Integer coolPoolSize, Integer maxPoolSize, Integer keepAliveTime, TimeUnit timeUnit,
                           Integer taskQueueSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        this.EXECUTOR = new ThreadPoolExecutor(coolPoolSize, maxPoolSize, keepAliveTime, timeUnit,
                new LinkedBlockingQueue<>(taskQueueSize), handler);
        this.executor.allowCoreThreadTimeOut(true);
    }


    /**
     * 获取executor对象
     * @return
     */
    public static ThreadPoolExecutor getExecutor() {
        return getInstance().executor();
    }


    /**
     * 线程安全创建线程池对象
     * @return FlowWorkerPoolFactory
     */
    private static ThreadPoolUtil getInstance() {

        if (null == INSTANCE) {
            synchronized (ThreadPoolUtil.class) {
                if (null == INSTANCE) {
                    INSTANCE =  new ThreadPoolUtil(DEFAULT_CORE_POOL_SIZE, DEFAULT_MAX_POOL_SIZE, DEFAULT_FREE_THREAD_KEEP_ALIVE,
                            TimeUnit.SECONDS, DEFAULT_TASK_QUEUE_SIZE, 
new CustomizableThreadFactory(THREAD_POOL_PRE_NAME), DEFAULT_REJECTED_HANDLER);
                }
            }
        }
        return INSTANCE;
    }

    /**
     * 获取executor对象
     * @return
     */
    private ThreadPoolExecutor executor() {
        return this.EXECUTOR;
    }
}

2、执行任务

ThreadPoolUtil.getExecutor().execute(() -> getUserName(id));

五、线程池的七个核心参数

参数 说明
corePoolSize 线程池核心线程大小
maxiPoolSize 线程池最大线程数量
keepAliveTime 空闲线程存活时间
unit 时间格式 , keepAliveTime 参数的时间单位
workQueue

工作队列(1.ArrayBlocking 2.LinkedBlockingQueue 3.SynchronizedQueue 4.PriorityBlockingQueue)

更多详情可看:

进来学习 BlockingQueue 阻塞队列

ThreadFactory 线程工厂
Handler 拒绝策略


其中拒绝策略分为四种:

  • AbortPolicy:中断抛出异常

    【默认策略】
  • DiscardPolicy:默默丢弃任务,不进行任何通知
  • DiscardOldestPolicy:丢弃掉在队列中存在时间最久的任务
  • CallerRunsPolicy:让提交任务的线程去执行任务

除上面四种之外,还可以自定义拒绝策略:

/** 自定义个拒绝策略 **/
    private static final RejectedExecutionHandler DEFAULT_REJECTED_HANDLER = new LogPolicy();



 public static class LogPolicy implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
           log.error("threadNamePrefix {}, corePoolSize {}, maxPoolSize {}, workQueueSize {}, rejected task {}.", new Object[]{THREAD_POOL_PRE_NAME, executor.getCorePoolSize(), executor.getMaximumPoolSize(), executor.getQueue().size(), r.toString()});

        }
}

六、线程池的执行流程



​​​​​​​



版权声明:本文为ZhangXS9722原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。