多线程之线程池ThreadPool


前言

当我们需要进行大量操作时,单线程同步操作的速率明显不能满足我们的需求。这时我们就要使用多线程技术,用多个线程同时处理多个任务,如各个线程之间又没有数据交流,更是可以使用异步方法加快任务的执行。
而线程的创建和销毁都需要占用资源,这时就要用到线程池技术。


一、线程池的运行流程

线程池的作用就是预先创建几个线程(核心线程),核心线程的生命周期与线程池一致,随着线程池的创建而创建,随着线程池的销毁而销毁
当核心线程被占满,新提交的任务会排列在阻塞队列中,核心线程空闲后,会立即处理阻塞队列中的任务。
当线程执行完分配到的任务,又会回到线程池中等待下一次调用。
当阻塞队列为空、线程空闲超过一个特定的时间,线程池就会销毁多余的线程,保留还在运行的线程,且至少保留“核心线程数”个线程。
所以,使用线程池技术只需要定义线程池中的几个属性,线程池会自动为你完成 分配任务、创建线程、空闲线程复用、空闲线程回收 等任务。

在这里插入图片描述

二、线程池实现类ThreadPoolTaskExcutor

1.引入库

代码如下(示例):

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

2.线程池七大参数

线程池七大参数分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler

(1)corePoolSize:线程池中常驻核心线程数

(2)maximumPoolSize:线程池能够容纳同时执行的最大线程数

(3)keepAliveTime:多余的空闲线程存活时间

(4)unit:keepAliveTime的时间单位

(5)workQueue:任务队列,被提交但尚未执行的任务

(6)threadFactory:表示生成线程池中的工作线程的线程工厂

(7)handler:拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何拒绝。


3.工具类代码

实例:

public class ExecutorUtil {
    //获取当前机器的核数
//    public static final int cpuNum = Runtime.getRuntime().availableProcessors();

    public static Executor getAsyncExecutor(Integer corePoolSize, Integer maxPoolSize) {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setMaxPoolSize(maxPoolSize);//最大线程大小
        taskExecutor.setCorePoolSize(corePoolSize);//核心线程大小
        //队列最大容量,默认为Integer的最大值,全程异步队列设为0
//        taskExecutor.setQueueCapacity(0);
        taskExecutor.setQueueCapacity(1000);
        //当提交的任务个数大于QueueCapacity,就需要设置该参数,但spring提供的都不太满足业务场景,可以自定义一个,也可以注意不要超过QueueCapacity即可
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);// 设置关闭时是否等待任务完成
        taskExecutor.setAwaitTerminationSeconds(60);// 等待终止的秒数,就是上面说的空闲多长时间会销毁
        taskExecutor.setThreadNamePrefix("Thread-");// 设置线程名前缀
        taskExecutor.initialize();
        return taskExecutor;
    }
}

4.设置参数技巧

(1)核心线程数和最大线程数:

CPU密集型:
最大线程数 = CPU核心数 + 1
核心线程数 = 最大线程数 * 20%

io密集型:
最大线程数 = CPU核心数 * 2
核心线程数 = 最大线程数 * 20%

最好让有丰富经验的人根据实际情况指定两个参数。

(2)阻塞队列:
如果全程异步可设置为0(线程之间完全独立,不存在数据交换,也无需等待同步信号返回)。
如果设置一个非零的数比如1000,阻塞队列的最大数量就为1000,加上最大线程数,也就保持在1000左右,有效限制并发量。
如果不设置阻塞队列,则默认为Integer的最大值,不建议这样设置,当并发量小的时候,无法填满阻塞队列,也就无法启用非核心线程;当能填满阻塞队列时,并发量就很大了,服务器的压力也会很大。所以还是建议设置一个阻塞队列长度,最好还是根据并发量和计算机性能设置一个合适的值。


5.具体使用

import org.example.util.ExecutorUtil;
import java.util.concurrent.Executor;

Executor executor = ExecutorUtil.getAsyncExecutor(CORE_POOL_NUM, MAX_POOL_NUM);
……// 此处应该是一个循环体for或while,重复提交任务到线程池
	executor.execute(() -> {
		……// 需要并发执行的方法
		});

总结

本文描述了线程池的原理与基本流程,提供了模板代码,希望能在各位读者需要时派上用场!

猜你喜欢

转载自blog.csdn.net/War_wick/article/details/129164138