ThreadPoolExecutor简单理解

本文详细介绍了Java线程池的使用方法,包括线程池参数的意义、等待队列的工作原理、线程工厂及拒绝策略的自定义实现。此外,还深入解析了线程池执行流程,并提供了完整的示例代码。

线程池

ThreadPoolExecutor

1. 线程池的使用
package com.radish.juc;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description TestThreadPoolExecutor
 * @Author Radish
 * @Date 2020-09-25 10:19
 */
public class TestThreadPoolExecutor {

    //创建自定义工厂,生产线程
    private static class MyThreadFactory implements ThreadFactory {

        private AtomicInteger count = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable r) {
            int andIncrement = count.getAndIncrement();
            System.out.println("------------ new Thread:ThreadPool-1:MyThread-" + andIncrement + " ------------");
            return new Thread(r, "ThreadPool-1:MyThread-" + andIncrement);
        }
    }

    //自定义拒绝策略
    private static class MyRejectHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            threadPoolStatus(executor, "任务提交失败");
            System.out.println("Oh, I am rejected...sad    ...-.-...");
        }
    }

    public static void main(String[] args) throws Exception {

        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                2,
                5,
                60,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10),
                new MyThreadFactory(),
                new MyRejectHandler()
        );

        for (int i = 0; i < 16; i++) {
            poolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " - workQueue.capacity:" + poolExecutor.getQueue().remainingCapacity());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }
    
    //只是一个输出方法,查看线程池当前状态
    public static void threadPoolStatus(ThreadPoolExecutor executor, String name) {
        BlockingQueue<Runnable> queue = executor.getQueue();
        System.out.println(Thread.currentThread().getName() + " -- " + name + " --: " +
                " 核心线程数:" + executor.getCorePoolSize() +
                " 活动线程数:" + executor.getActiveCount() +
                " 最大线程数:" + executor.getMaximumPoolSize() +
                " 线程池活跃度:" + divide(executor.getActiveCount(), executor.getMaximumPoolSize()) +
                " 任务完成数:" + executor.getCompletedTaskCount() +
                " 队列大小:" + (queue.size() + queue.remainingCapacity()) +
                " 当前排队线程数:" + queue.size() +
                " 队列剩余大小:" + queue.remainingCapacity() +
                " 队列使用度:" + divide(queue.size(),queue.size() + queue.remainingCapacity()));
    }

    public static String divide(int num1, int num2) {
        return String.format("%1.2f%%",Double.parseDouble(num1 + "")/Double.parseDouble(num2 + "") * 100);
    }
}

将程序运行起来,输出如下:

------------ new Thread:ThreadPool-1:MyThread-1 ------------
------------ new Thread:ThreadPool-1:MyThread-2 ------------
------------ new Thread:ThreadPool-1:MyThread-3 ------------
ThreadPool-1:MyThread-1 - workQueue.capacity:0
ThreadPool-1:MyThread-2 - workQueue.capacity:0
------------ new Thread:ThreadPool-1:MyThread-4 ------------
ThreadPool-1:MyThread-3 - workQueue.capacity:0
------------ new Thread:ThreadPool-1:MyThread-5 ------------
ThreadPool-1:MyThread-4 - workQueue.capacity:0
ThreadPool-1:MyThread-5 - workQueue.capacity:0
main -- 任务提交失败 --:  核心线程数:2 活动线程数:5 最大线程数:5 线程池活跃度:100.00% 任务完成数:0 队列大小:10 当前排队线程数:10 队列剩余大小:0 队列使用度:100.00%
Oh, I am rejected...sad    ...-.-...
ThreadPool-1:MyThread-2 - workQueue.capacity:1
ThreadPool-1:MyThread-1 - workQueue.capacity:2
ThreadPool-1:MyThread-3 - workQueue.capacity:4
ThreadPool-1:MyThread-5 - workQueue.capacity:5
ThreadPool-1:MyThread-4 - workQueue.capacity:4
ThreadPool-1:MyThread-2 - workQueue.capacity:7
ThreadPool-1:MyThread-1 - workQueue.capacity:7
ThreadPool-1:MyThread-5 - workQueue.capacity:8
ThreadPool-1:MyThread-3 - workQueue.capacity:10
ThreadPool-1:MyThread-4 - workQueue.capacity:10

2. 创建线程池时每个参数的意义
  • corePoolSize – 即使处于空闲状态,池中也会保留的线程数量(除非设置了allowCoreThreadTimeOut这个参数)

  • maximumPoolSize – 池中所允许的最大线程数量

  • keepAliveTime – 当线程数量大于核心线程数时,那些空闲线程的存活等待时间

  • unit – 参数 keepAliveTime 的单位

  • workQueue – 存储待执行任务的队列

  • threadFactory – 线程工厂

  • handler – 拒绝策略,当任务提交而线程都忙且队列已满时触发

3. 等待队列

  BlockingQueue是一个接口类,实现类有以下这些(图片来自美团…)

img

4. 线程工厂

  因为默认的线程池创建的线程名字并不规范,当系统出现问题时,比较难定位问题,所以可以通过自定义线程池来为线程指定名称以及其他你需要的操作。

实现ThreadFactory并重写newThread方法即可。

5. 拒绝策略

  ThreadPoolExecutor中已经包含四种拒绝策略。

  • CallerRunsPolicy 由调用线程处理该任务,可以保证全部执行完成。
  • AbortPolicy 抛弃任务并抛出异常,可以在系统不能承受太大压力的时候使用,我们也可以发现异常进行处理
  • DiscardPolicy 悄咪咪地扔掉任务,e…可以用来处理无关紧要的任务
  • DiscardOldestPolicy 悄咪咪地扔掉最老的任务
  • 也可以像demo中那样,自定义拒绝策略,像是写日志啊存数据啊这种需求,都可以自己来实现
    /**
     * A handler for rejected tasks that runs the rejected task
     * directly in the calling thread of the {@code execute} method,
     * unless the executor has been shut down, in which case the task
     * is discarded.
     */
    public static class CallerRunsPolicy implements RejectedExecutionHandler{...}

    /**
    * A handler for rejected tasks that throws a
    * {@code RejectedExecutionException}.
    */
    public static class AbortPolicy implements RejectedExecutionHandler{...}

	/**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     */
    public static class DiscardPolicy implements RejectedExecutionHandler{...}
        
    /**
     * A handler for rejected tasks that discards the oldest unhandled
     * request and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded.
     */
    public static class DiscardOldestPolicy implements RejectedExecutionHandler{...}
    
6. 线程池执行步骤

  光知道用法肯定是不行滴,还得知道它是怎样来执行的,下面就是submit方法的源码

/**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

  嗯,它创建了一个新任务,然后交给execute去执行了,那就看看execute

/**
     * Executes the given task sometime in the future.  The task
     * may execute in a new thread or in an existing pooled thread.
     
     提交的任务可能会通过新建一个线程来执行,也可能是由线程池中的线程来执行
     
     *
     * If the task cannot be submitted for execution, either because this
     * executor has been shutdown or because its capacity has been reached,
     * the task is handled by the current {@code RejectedExecutionHandler}.
     
     如果由于线程池已经shutdown或者线程池容量已满,造成任务不能提交的情况,就会由拒绝策略来处理
     
     *
     * @param command the task to execute
     * @throws RejectedExecutionException at discretion of
     *         {@code RejectedExecutionHandler}, if the task
     *         cannot be accepted for execution
     * @throws NullPointerException if {@code command} is null
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        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))
            reject(command);
    }

这段即使代码看不懂,注释也写的很清楚了,此时我们只需要打开翻译软件便可以获得今日的快乐

简单描述一下执行步骤:

  1. 如果当前线程数<corePoolSize,创建一个核心线程并将当前任务作为它的第一个任务处理

  2. 如果核心线程已全部创建,将任务放到workQueue中

  3. 如果workQueue已满,查看当前工作线程数量未达到maximumPoolSize,创建一个非核心线程;

    如果workQueue已满,且当前工作线程数量已达到maximumPoolSize,则该任务提交失败,启用拒绝策略处理该任务

参考文献:

别人家的博客

美团技术团队

《Java并发编程的艺术》

  脑袋晕晕的,昨天睡的不太好,又开始焦虑了…呀!振作一点呀!累了就出去走走吧本来想写多一点的,但是感觉脑袋一团乱,就先写一下比较基础的部分吧

  不过!虽然脑袋有点晕,但是不影响我对美团技术的喜爱!美团技术团队的博客写的真的很好!强烈推荐!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值