Java线程池的使用(合理设置线程池的大小)

线程池工具类

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.所有任务执行完,线程数降到核心线程数

----------------------------------------------分割线----------------------------------------------

如果是局部使用线程池需要shutdown();

全局使用不需要shutdown();

 线程池工具类参考:自定义线程池工具类_朱守成-CSDN博客_自定义线程池工具类

 合理设置线程池参考:Java-如何合理的设置线程池大小 - 晋级在路上 - 博客园

线程池的执行过程:线程池参数以及运行过程_cajole_zero的博客-CSDN博客

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadPoolExecutor线程池的阻塞队列大小决定了可以容纳等待执行的任务数量。在ThreadPoolExecutor中,可以使用不同的阻塞队列实现来控制任务的排队和执行。 常用的阻塞队列实现有以下几种: 1. 直接提交队列(SynchronousQueue):该队列不存储任务,而是直接将任务提交给线程池中的工作线程进行执行,如果没有可用的工作线程,则尝试创建一个新线程来执行任务。这种情况下,设置队列大小为0或者1没有意义。 2. 有界队列(ArrayBlockingQueue):该队列有固定的容量,可以在创建ThreadPoolExecutor时指定队列大小。当线程池中的线程数达到最大线程数时,后续的任务会被放入到该队列等待执行。 3. 无界队列(LinkedBlockingQueue):该队列没有固定的容量,可以根据需要动态地增加其大小。当线程池中的线程数达到最大线程数时,后续的任务会被放入到该队列等待执行。因为队列没有大小限制,所以可能会导致内存溢出。 4. 优先级队列(PriorityBlockingQueue):该队列根据任务的优先级进行排序,具有更高优先级的任务会被优先执行。可以在创建ThreadPoolExecutor时指定比较器来定义任务的优先级。 需要根据具体的场景和需求选择合适的阻塞队列实现和大小设置。如果任务量较大,可以选择有界队列或者优先级队列,以控制线程池的负载。如果任务量不确定或者需要动态增加队列大小,可以选择无界队列

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值