ThreadPoolExecutor源码学习以及观雄哥大佬博客有感

前言

  今天看了下雄哥的Java如何让线程池满后再放队列,虽然说看完实现有点骚,但是感叹到我,这招曲线救国很强。

ThreadPookExecutor部分源码

execute(Runnable command)

看下下面中文注释

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.
         */
         //AtomicInteger进行计数
        int c = ctl.get();
        //线程数小于核心线程数,就是新建线程,丢到线程池里面addWorker().然后里面会对计数进行+1
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //如果线程池还在跑,尝试丢到队列里头,通过判断offer方法来判断是否队列已经满了,这个也是为啥无界队列会导致cpu爆的原因,一直往队列插入。
        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);
        }
        //如果到达最大线程数的话,会抛出异常RejectedExecutionException
        else if (!addWorker(command, false))
            reject(command);
    }

RejectedExecutionException是不是有点熟悉,当任务太多的时候,线程池会抛出这个异常也可以理解为线程池的拒绝策略。

观后感

  看了雄哥的那篇博客,是先达到最大线程数再往队列里头插~

  我们来看下是怎么实现的

重写线程池

public class EagerThreadPoolExecutor extends ThreadPoolExecutor {

    /**
     * 定义一个成员变量,用于记录当前线程池中已提交的任务数量
     */
    private final AtomicInteger submittedTaskCount = new AtomicInteger(0);

    public EagerThreadPoolExecutor(int corePoolSize,
                                   int maximumPoolSize,
                                   long keepAliveTime,
                                   TimeUnit unit, TaskQueue<Runnable> workQueue,
                                   ThreadFactory threadFactory,
                                   RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }


    public int getSubmittedTaskCount() {
        return submittedTaskCount.get();
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        // ThreadPoolExecutor的勾子方法,在task执行完后需要将池中已提交的任务数 - 1
        submittedTaskCount.decrementAndGet();
    }

    @Override
    public void execute(Runnable command) {
        if (command == null) {
            throw new NullPointerException();
        }
        // do not increment in method beforeExecute!
        // 将池中已提交的任务数 + 1
        submittedTaskCount.incrementAndGet();
        try {
            super.execute(command);
        } catch (RejectedExecutionException rx) {
            // retry to offer the task into queue.
            final TaskQueue queue = (TaskQueue) super.getQueue();
            try {
                if (!queue.retryOffer(command, 0, TimeUnit.MILLISECONDS)) {
                    submittedTaskCount.decrementAndGet();
                    throw new RejectedExecutionException("Queue capacity is full.", rx);
                }
            } catch (InterruptedException x) {
                submittedTaskCount.decrementAndGet();
                throw new RejectedExecutionException(x);
            }
        } catch (Throwable t) {
            // decrease any way
            submittedTaskCount.decrementAndGet();
            throw t;
        }
    }
}

主要代码

  super.execute(command);这就是曲线救国的方法,通过执行原线程池的执行方法,那么之前ThreadPoolExecute的执行顺序怎么办?

  这里涉及上面源码解读里头的workQueue.offer(command),如果它为false的时候,表示插入不了,那么在在线程数超过核心线程数,没有超过最大数量时,会抛出RejectedExecutionException异常。

  那么雄哥的实现是捕获异常,塞到队列里头。当它为false的时候,表示队列也满了,抛出RejectedExecutionException异常。

  我感觉这操作很骚,但是又很强,哈哈,牛逼牛逼~

  那么这里涉及队列的offer返回false的操作(看上面加粗的字的解释,为啥需要返回false

重写队列

public class TaskQueue<R extends Runnable> extends LinkedBlockingQueue<Runnable> {

    private static final long serialVersionUID = -2635853580887179627L;
    
    // 自定义的线程池类,继承自ThreadPoolExecutor
    private EagerThreadPoolExecutor executor;

    public TaskQueue(int capacity) {
        super(capacity);
    }

    public void setExecutor(EagerThreadPoolExecutor exec) {
        executor = exec;
    }

    // offer方法的含义是:将任务提交到队列中,返回值为true/false,分别代表提交成功/提交失败
    @Override
    public boolean offer(Runnable runnable) {
        if (executor == null) {
            throw new RejectedExecutionException("The task queue does not have executor!");
        }
        // 线程池的当前线程数
        int currentPoolThreadSize = executor.getPoolSize();
        if (executor.getSubmittedTaskCount() < currentPoolThreadSize) {
            // 已提交的任务数量小于当前线程数,意味着线程池中有空闲线程,直接扔进队列里,让线程去处理
            return super.offer(runnable);
        }

        // return false to let executor create new worker.
        if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
            // 重点: 当前线程数小于 最大线程数 ,返回false,暗含入队失败,让线程池去创建新的线程
            return false;
        }

        // 重点: 代码运行到此处,说明当前线程数 >= 最大线程数,需要真正的提交到队列中
        return super.offer(runnable);
    }

    public boolean retryOffer(Runnable o, long timeout, TimeUnit unit) throws InterruptedException {
        if (executor.isShutdown()) {
            throw new RejectedExecutionException("Executor is shutdown!");
        }
        return super.offer(o, timeout, unit);
    }
}

重点代码

// return false to let executor create new worker.
        if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
            // 重点: 当前线程数小于 最大线程数 ,返回false,暗含入队失败,让线程池去创建新的线程
            return false;
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值