ThreadPoolExecutor使用详解

一、核心构造方法参数讲解 

参数名作用
corePoolSize核心线程池大小
maximumPoolSize最大线程池大小
keepAliveTime线程池中超过corePoolSize数目的空闲线程最大存活时间;可以allowCoreThreadTimeOut(true)使得核心线程有效时间
TimeUnitkeepAliveTime时间单位
workQueue阻塞任务队列
threadFactory新建线程工厂
RejectedExecutionHandler当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理

重点讲解: 
其中比较容易让人误解的是:corePoolSize,maximumPoolSize,workQueue之间关系。 

1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。 
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行 
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务 
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理 
5.当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程 
6.当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭 

定制属于自己的非阻塞线程池 

@Slf4j
public class CustThreadPoolExecutor {

    private ThreadPoolExecutor pool = null;
    private static final int CORE_POOL_SIZE = 10;
    private static final int MAXIMUM_POOL_SIZE = 20;
    private static final int KEEP_ALIVE_TIME = 30;

    /**
     * 线程池初始化方法
     *
     * corePoolSize     核心线程池大小----10
     * maximumPoolSize  最大线程池大小----20
     * keepAliveTime    线程池中超过corePoolSize数目的空闲线程最大存活时间----30+单位TimeUnit
     * TimeUnit         keepAliveTime时间单位----TimeUnit.MINUTES
     * workQueue        阻塞队列----new ArrayBlockingQueue<>(10)====10容量的阻塞队列
     * threadFactory    新建线程工厂----new CustomThreadFactory()====定制的线程工厂
     * rejectedExecutionHandler 当提交任务数超过maxmumPoolSize+workQueue之和时,
     * 							即当提交第31个任务时(前面线程都没有执行完,此测试方法中用sleep(100)),
     * 						          任务会交给RejectedExecutionHandler来处理
     */
    private void init() {
        pool = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.MINUTES,
                new ArrayBlockingQueue<>(10),
                new CustomThreadFactory(),
                new CustomRejectedExecutionHandler());
    }

    public void destroy() {
        if(pool != null) {
            //pool.shutdownNow(); // 立刻关闭线程池,提交的任务没有全部执行
            pool.shutdown(); // 优雅关闭线程池,没有被拒绝的任务都可执行
        }
    }

    public ExecutorService getCustThreadPoolExecutor() {
        return this.pool;
    }

    // 自定义线程工厂
    private class CustThreadFactory implements ThreadFactory {

        private AtomicInteger count = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            String threadName = CustThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);
            System.out.println(threadName);
            t.setName(threadName);
            return t;
        }
    }

    // 实现RejectedExecutionHandler接口,自定义拒绝策略
    private class CustRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            // 记录异常、报警处理等
            //log.info("ERROR..................................................");
            System.out.println("ERROR..................................................");
        }
    }

    // 测试构造的线程池
    public static void main(String[] args) {
        CustThreadPoolExecutor exec = new CustThreadPoolExecutor();
        // 1.初始化
        exec.init();

        // 2、执行任务
        ExecutorService pool = exec.getCustThreadPoolExecutor();
        for(int i=1; i<50; i++) {
            //log.info("提交第" + i + "个任务!");
            System.out.println("提交第" + i + "个任务!");
            pool.execute(() -> {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                }
                System.out.println("task running=====");
            });
        }

        // 3.销毁
        exec.destroy();

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

方法中建立一个核心线程数为10个,缓冲队列有10个的线程池。每个线程任务,执行时会先睡眠3秒,保证提交10任务时,线程数目被占用完,再提交20任务时,阻塞队列被占用完,这样提交第31个任务是,会交给CustRejectedExecutionHandler 异常处理类来处理。

public void execute(Runnable command) {
        // 如果提交的任务为空,则抛出空指针异常
        if (command == null)
            throw new NullPointerException();

        1、
        int c = ctl.get();// 获取线程池的状态和线程池中线程的数量
        if (workerCountOf(c) < corePoolSize) {// 线程池中的线程数量小于corePoolSize的值
            // 重新开启线程执行任务
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        2、// 如果线程池处于RUNNING状态,并且入队成功,即队列未满
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();// 再次获取线程池的状态和线程池中线程的数量,二次检查
            // 如果线程池没有未处于RUNNING状态,回滚,从队列中删除任务
            if (! isRunning(recheck) && remove(command))
                reject(command);// 执行拒绝策略
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);// 如果线程池为空,则向线程池中添加一个线程
        }
        3、// 任务队列已满,则尝试新增worker线程,如果新增线程失败,则执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }
整个任务的执行流程,我们可以简化成下图所示:

注意:31以后提交的任务就不能正常处理了。因为,execute中提交到任务队列是用的offer方法,如上面代码,这个方法是非阻塞的,所以就会交给CustRejectedExecutionHandler 来处理。所以对于大数据量的任务来说,这种线程池,如果不设置队列长度会OOM。设置队列长度,会有任务得不到处理,接下来我们构建一个阻塞的自定义线程池

定制属于自己的阻塞线程池 

public class CustThreadPoolExecutor2 {

    private ThreadPoolExecutor pool = null;
    private static final int CORE_POOL_SIZE = 1;
    private static final int MAXIMUM_POOL_SIZE = 3;
    private static final int KEEP_ALIVE_TIME = 30;

    /**
     * 线程池初始化方法
     */
    private void init() {
        pool = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.MINUTES,
                new ArrayBlockingQueue<>(10),
                new CustomThreadFactory(),
                new CustomRejectedExecutionHandler());
    }

    public void destroy() {
        if(pool != null) {
            pool.shutdown();
        }
    }

    public ExecutorService getCustThreadPoolExecutor() {
        return this.pool;
    }

    private class CustThreadFactory implements ThreadFactory {
        private AtomicInteger count = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            String threadName = CustThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);
            System.out.println(threadName);
            t.setName(threadName);
            return t;
        }
    }

    private class CustRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                // 核心改造点,由blockingQueue的offer改阻塞成put阻塞方法
                // 如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续
                // 这样所提交的任务全部有机会执行
                executor.getQueue().put(r);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 测试构造的线程池
    public static void main(String[] args) {

        CustThreadPoolExecutor2 exec = new CustThreadPoolExecutor2();
        // 1.初始化
        exec.init();

        ExecutorService pool = exec.getCustThreadPoolExecutor();
        for(int i=1; i<=40; i++) {
            System.out.println("提交第" + i + "个任务!");
            pool.execute(() -> {
                try {
                    System.out.println(">>>task is running=====");
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        exec.destroy();

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

源码:当提交任务被拒绝时,进入拒绝机制,我们实现拒绝方法,把任务重新用阻塞提交方法put提交,实现阻塞提交任务功能,防止队列过大,OOM,提交被拒绝方法在下面

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            // 进入拒绝机制, 我们把runnable任务拿出来,重新用阻塞操作put,来实现提交阻塞功能
            reject(command);
    }

如果对你有帮助帮忙点个赞哈

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ThreadPoolExecutorJava 提供的用于创建线程池的类,它的构造函数有很多参数,下面是对这些参数的详细解释: 1. corePoolSize:线程池中核心线程的数量。当线程池中的线程数量小于 corePoolSize 时,新的任务会一直创建新的线程直到达到 corePoolSize 个线程。 2. maximumPoolSize:线程池中最大线程数。当线程池中的线程数量达到 corePoolSize 后,新的任务会被放入到等待队列中,等待被执行。如果等待队列已满,且线程池中的线程数量小于 maximumPoolSize,则会创建新的线程执行任务。 3. keepAliveTime:线程池中非核心线程的超时时间。当线程池中的线程数量大于 corePoolSize 时,多余的线程会被回收,但回收前会等待 keepAliveTime 时间,如果在这个时间内没有新的任务需要执行,则这个线程会被终止。 4. TimeUnit:超时时间的单位。 5. workQueue:用于缓存等待执行的任务的队列。ThreadPoolExecutor 提供了多种队列,如 ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue 等。可以根据需求选择不同的队列。 6. threadFactory:用于创建新的线程。ThreadPoolExecutor 默认使用 Executors.defaultThreadFactory() 创建线程。如果需要自定义线程创建方式,可以实现 ThreadFactory 接口。 7. handler:线程池中的线程数量达到 maximumPoolSize,并且等待队列已满时,新的任务的处理策略。ThreadPoolExecutor 提供了 4 种策略: - AbortPolicy:直接抛出异常; - CallerRunsPolicy:不在新线程中执行任务,而是让调用 execute 方法的线程执行任务; - DiscardOldestPolicy:丢弃最老的任务,执行当前任务; - DiscardPolicy:直接丢弃任务。 这些参数可以根据实际需求进行调整,以达到最优的线程池效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡布奇诺-海晨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值