使用ThreadPoolExecutor实现控制一个方法的并发量,实现同时只能有10个线程进入方法

本文详细介绍了线程池的概念及其核心组件ThreadPoolExecutor的使用方法。包括线程池的设计目的、参数配置、饱和策略等关键信息,并通过示例代码展示了如何创建和使用线程池。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 

线程池是什么

ThreadPoolExecutor

实现代码

需要注意的点


线程池是什么

线程池是一种基于池化思想管理线程的工具,设计它的核心目的是解决资源管理问题,可以降低资源消耗、提高响应速度以及提高线程的可管理性。

ThreadPoolExecutor

ThreadPoolExecutor是Executor最下层的实现类

 

  • 参数1:corePoolSize:核心池大小
  • 参数2:maximumPoolSize 线程池中允许的最大线程数
  • 参数3:keepAliveTime 线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间,默认情况下,该参数只在线程数大于corePoolSize时才有用
  • 最终任务执行完了,线程池中只会保留核心池大小数目的线程
  • 参数4:unit 时间的单位 枚举类型
  • 参数5:workQueue 工作的队列 存储任务的
  • 参数6:threadFactory 线程工厂 用来创建线程的 因为线程池里可能会有多个线程,由线程工厂来创建

线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

  •  AbortPolicy:直接抛出异常,默认策略;
  • CallerRunsPolicy:用调用者所在的线程来执行任务;
  • DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
  • DiscardPolicy:直接丢弃任务;

实现代码

public class ThreadTest {
    public static void main(String[] args) {
        // BlockingQueue<E> 是一个单端队列 
        // BlockingDeque<E> 是一个双端队列 可以从两端出队

        LinkedBlockingQueue<Runnable> linkedBlockingQueue = new LinkedBlockingQueue<>(
                100);// 100是任务队列的最大上限

        /***
         * 参数1:corePoolSize:核心池大小
         *
         * 参数2:maximumPoolSize最大池大小
         *
         * 参数3:keepAliveTime 保留存活的时间 如果过了这个存活时间,就会把除了位于核心池以外的线程集合中的其他线程给释放掉
         * 最终任务执行完了,线程池中只会保留核心池大小数目的线程
         *
         * 参数4:unit 时间的单位 枚举类型
         *
         * workQueue 工作的队列 存储任务的
         *
         * threadFactory 线程工厂 用来创建线程的 因为线程池里可能会有多个线程,由线程工厂来创建
         */
        ThreadFactory threadFactory = new ThreadFactory() {
            // 线程安全的一个整数类 它是一个包装类 线程安全的一个整数包装类
            // 参数 initialValue 是初始值
            AtomicInteger atomicInteger = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {

                // 创建一个线程 把r赋值给该线程
                Thread thread = new Thread(r);
                // 给线程起一个名称
                // 线程池就是为了解决多线程的问题,
                // 不能使用i++ 因为i++本身就是线程不安全的,因为i++底层的执行要分好几步
                // newThread方法是并发的 ,同时可能有多个线程同时执行这个方法
                // 也就有可能有多个线程同时执行i++,就有可能出现不同线程出现同样的名称

                // 如果给本方法添加synchronized 可以,但是影响性能,而且synchronized粒度比较大,不太适合计数器这种情形
                // atomicInteger.getAndIncrement() 先获取值 再自增
                thread.setName("myThread:" + atomicInteger.getAndIncrement());
                return thread;
            }
        };
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10,
                1000, TimeUnit.MILLISECONDS, linkedBlockingQueue, threadFactory);
        // maximumPoolSize+任务队列的大小为100 ,如果再多就会报拒绝异常
        for (int i = 0; i < 110; i++) {
            threadPoolExecutor.execute(new Runnable() {

                @Override
                public void run() {
                    try {
                        myMethod();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }

    }

    // 加static 静态只能访问静态
    public static void myMethod() throws InterruptedException {
        System.out.println("ThreadName = " + Thread.currentThread().getName()
                + "进入方法");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("ThreadName = " + Thread.currentThread().getName()
                + "离开方法");
        System.out.println("-----------------");

    }
}

结果

 

需要注意的点

有2个需要注意的点

  • 为什么我for循环了110次?

需要注意的是触发拒绝策略的条件是:线程数>线程池中允许的最大线程数+阻塞队列的大小

当前任务队列的大小为100,线程池中允许的最大线程数是10,所以是110

  • 我们知道任务类型分为cpu密集型和io密集型,针对不同类型的任务我们要合理配置线程池

cpu密集型一般配置Ncpu+1个线程的线程池,io密集型一般配置2*Ncpu,这里的线程数量是指的是线程池中允许的最大线程数

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值