线程池工具类

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 线程池工具类:
 * 处理项目中需要异步处理的任务,例如日志服务,监控服务等
 *
 * @author zsc
 * @datetime 2017年11月22日 上午11:04:09
 */
public class ThreadPoolUtil {

    /**
     * 工具类,构造方法私有化
     */
    private ThreadPoolUtil() {
        super();
    }

    // 线程池核心线程数
    private final static Integer COREPOOLSIZE = 5;
    // 最大线程数
    private final static Integer MAXIMUMPOOLSIZE = 10;
    // 空闲线程存活时间
    private final static Integer KEEPALIVETIME = 3 * 60;
    // 线程等待队列
    private static BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
    // 线程池对象
    private static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(COREPOOLSIZE, MAXIMUMPOOLSIZE,
            KEEPALIVETIME, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy());

    /**
     * 向线程池提交一个任务,返回线程结果
     *
     * @param r
     * @return
     */
    public static Future<?> submit(Callable<?> r) {
        return threadPool.submit(r);
    }

    /**
     * 向线程池提交一个任务,不关心处理结果
     *
     * @param r
     */
    public static void execute(Runnable r) {
        threadPool.execute(r);
    }

    /**
     * 停止任务
     */
    public static void shutdown() {
        threadPool.shutdown();
    }

    /**
     * 获取当前线程池线程数量
     */
    public static int getSize() {
        return threadPool.getPoolSize();
    }

    /**
     * 获取当前活动的线程数量
     */
    public static int getActiveCount() {
        return threadPool.getActiveCount();
    }
}

使用线程池

import com.ndmicro.common.utils.ThreadPoolUtil;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;

/**
 * @author lianJiaYu
 * @date 2021/11/8 11:46
 */
public class Test1 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        List<Integer> integers = new CopyOnWriteArrayList<>();
        //不使用线程池添加10W数据
//        long startTime = System.currentTimeMillis();   //获取开始时间
//        for (int j = 0; j < 100000; j++) {
//            integers.add(j);
//        }
//        long endTime = System.currentTimeMillis(); //获取结束时间
//        System.out.println("程序运行时间: " + (endTime - startTime) + "ms"); //程序运行时间: 3404ms


        //使用线程池添加10W数据(线程池下execute方法使用)
        long startTime = System.currentTimeMillis();   //获取开始时间
        ThreadPoolUtil.execute(() -> {
            for (int j = 0; j < 100000; j++) {
                integers.add(j);
            }
            System.out.println("===integers:" + integers.size());
        });
        ThreadPoolUtil.shutdown();
        long endTime = System.currentTimeMillis(); //获取结束时间
        System.out.println("程序运行时间: " + (endTime - startTime) + "ms"); //程序运行时间: 84ms


        //线程池下submit方法使用(带返回值)
//        long startTime = System.currentTimeMillis();   //获取开始时间
//        Future<?> submit = ThreadPoolUtil.submit(() -> {
//            for (int j = 0; j < 100000; j++) {
//                integers.add(j);
//            }
//            System.out.println("===integers:" + integers.size());
//            return "true";
//        });
//        ThreadPoolUtil.shutdown();
//        long endTime = System.currentTimeMillis(); //获取结束时间
//        System.out.println("程序运行时间: " + (endTime - startTime) + "ms"); //程序运行时间: 97ms
//        //获取返回值
//        String result = (String) submit.get();
//        System.out.println("===result:" + result);
//        if ("true".equals(result)) {
//            String name = Thread.currentThread().getName();
//            System.out.println("经过返回值比较,submit方法执行任务成功    thread name: " + name);
//        }
    }

设置 Java 线程池大小

配置线程池的大小可根据以下几个维度进行分析来配置合理的线程数:

  1. 任务性质可分为:CPU密集型任务,IO密集型任务,混合型任务。
  2. 任务的执行时长。
  3. 任务是否有依赖——依赖其他系统资源,如数据库连接等。
  • CPU密集型任务

尽量使用较小的线程池,一般为CPU核心数+1。 
因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。

  • IO密集型任务

可以使用稍大的线程池,一般为2*CPU核心数+1。 
因为IO操作不占用CPU,不要让CPU闲下来,应加大线程数量,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。

  • 混合型任务

可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。 
只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。 
因为如果划分之后两个任务执行时间相差甚远,那么先执行完的任务就要等后执行完的任务,最终的时间仍然取决于后执行完的任务,而且还要加上任务拆分与合并的开销,得不偿失

  • 依赖其他资源

如某个任务依赖数据库的连接返回的结果,这时候等待的时间越长,则CPU空闲的时间越长,那么线程数量应设置得越大,才能更好的利用CPU。 

借鉴别人的文章 对线程池大小的估算公式:

       最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。

//获取cpu核数大小
System.out.println(Runtime.getRuntime().availableProcessors());

线程池的执行过程

1.任务进来先创建核心线程,核心线程满了放入队列
2.队列满了创建普通线程
3.线程满了并且队列满了执行reject策略
4.无任务时普通线程会根据超时时间慢慢降到核心线程数,此过程中如果有任务进来,空闲线程去执行任务,线程都不空闲放入队列,重复步骤2
5.所有任务执行完,线程数降到核心线程数