博客
关于我
线程池的增加线程与拒绝线程的策略
阅读量:538 次
发布时间:2019-03-08

本文共 6747 字,大约阅读时间需要 22 分钟。

一 概述

由于系统线程池每天需要处理的任务线程不是固定不变的,会多会少,所以线程池存在自己的线程增加方式。当然,线程池的中线程的数量也是应该是有限的,因为过多的线程需要耗费很多的系统资源。

二 线程池增加线程的方式

  1. 当线程池中线程数小于核心线程数(corePoolSize)的时候,即使线程池中之前的任务线程处于空闲状态也会创建一个新的线程来执行新的任务。
  2. 当线程池中的线程数等于(或大于)核心线程数(corePoolSize)同时多余的线程数小于(或等于存储队列(workQueue))的时候会将多余的线程放入存储队列中(前提存储队列中是可以存放线程的)。
  3. 当多余线程数又超过存储队列(workQueue)的容量同时不超过线程数最大值maxinumPoolSize的时候会创建新的线程来执行线程任务。
  4. 当线程池中的线程任务请求超过或者刚好等于线程池中线程最大值maxinumPoolSize的时候线程池就会拒绝新请求的任务线程。

线程池接收到任务线程后的完整操作流程:

三 线程池拒绝线程任务的策略

线程池构造函数中的接口参数import java.util.concurrent.RejectedExecutionHandler为我提供了线程池拒绝线程任务策略,下图中展示的是import java.util.concurrent.RejectedExecutionHandler结构的实现类,即线程池拒绝线程任务策略的具体实现。

RejectHandler

private static class RejectHandler implements RejectedExecutionHandler {    private RejectHandler() {        }    public void rejectedExecution(Runnable r, java.util.concurrent.ThreadPoolExecutor executor) {            //直接抛出异常            throw new RejectedExecutionException();    }}

RejectHandler是接口import java.util.concurrent.RejectedExecutionHandler在Tomcat中实现类。该实现类仅仅是通方法rejectedExecution方法直接抛出拒绝执行异常。

AbortPolicy(线程池默认)

/**     * The default rejected execution handler     */    private static final RejectedExecutionHandler defaultHandler =        new AbortPolicy();     /**     * A handler for rejected tasks that throws a     * {@code RejectedExecutionException}.     */    public static class AbortPolicy implements RejectedExecutionHandler {        /**         * Creates an {@code AbortPolicy}.         */        public AbortPolicy() { }        /**         * Always throws RejectedExecutionException.         *         * @param r the runnable task requested to be executed         * @param e the executor attempting to execute this task         * @throws RejectedExecutionException always         */        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {            throw new RejectedExecutionException("Task " + r.toString() +                                                 " rejected from " +                                                 e.toString());        }    }

该拒绝策略直接通过方法rejectedExecution抛出解决执行异常,r 为执行的请求执行的可执行任务,e为试图执行此任务的执行者,异常信息为可执行任务r为某可执行者e拒绝了。

CallerRunsPolicy

/**     * 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 {        /**         * Creates a {@code CallerRunsPolicy}.         */        public CallerRunsPolicy() { }        /**         * Executes task r in the caller's thread, unless the executor         * has been shut down, in which case the task is discarded.         *         * @param r the runnable task requested to be executed         * @param e the executor attempting to execute this task         */        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {            if (!e.isShutdown()) {                r.run();//提交任务的由提交任务的线程自己通过run方法执行            }        }    }

该拒绝策略不同于上述两种拒绝策略直接拒绝执行而抛出异常,而是先判断线程池是否被停止。如果是没有被停止的情况,就会由任务线程r自己使用run方法执行。通俗的来说,该拒绝策略为当线程池无法处理当前提交的线程任务,同时提交该线程任务的时主线程时,则该任务就会由主线程执行。这样处理可以在避免任务损失的同时,降低了提交任务的速度,从而给线程池一些缓冲的时间。

DiscardPolicy

/**     * A handler for rejected tasks that silently discards the     * rejected task.     */    public static class DiscardPolicy implements RejectedExecutionHandler {        /**         * Creates a {@code DiscardPolicy}.         */        public DiscardPolicy() { }        /**         * Does nothing, which has the effect of discarding task r.         *         * @param r the runnable task requested to be executed         * @param e the executor attempting to execute this task         */        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {        }    }

该拒绝策略是一个会悄悄的拒绝任务线程,又不发出任何通知的狠角色,真是人狠话不多。

DiscardOldestPolicy

/**     * 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 {        /**         * Creates a {@code DiscardOldestPolicy} for the given executor.         */        public DiscardOldestPolicy() { }        /**         * Obtains and ignores the next task that the executor         * would otherwise execute, if one is immediately available,         * and then retries execution of task r, unless the executor         * is shut down, in which case task r is instead discarded.         *         * @param r the runnable task requested to be executed         * @param e the executor attempting to execute this task         */        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {            if (!e.isShutdown()) {                //队列是一种先进先出的数据结构,所以队首的任务存活时间最长                e.getQueue().poll(); //丢弃存储队列中存活时间最长的任务线程                e.execute(r);//尝试处理新的任务线程            }        }    }

getQueue()

//获取存储队列,该队列为阻塞队列   public BlockingQueue
getQueue() { return workQueue; }

execute(Runnable r)

public void execute(Runnable command) {        if (command == null)            throw new NullPointerException();           int c = ctl.get();                //第一步首先检测是否小于核心线程数        if (workerCountOf(c) < corePoolSize) {            //增加一个线程,addWorker增加一个工作线程,command即为我们的任务            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);    }

execute(Runnable commond)方法执行过程被分为三步:

  1. 第一步,如果正在运行的线程少于corePoolSize线程,先尝试使用给定任务作为其第一个任务来启动新线程。对addWorker的调用从原子上检查工作线程的运行状态(runState)和已经存在的任务数量(workerCount),从而通过返回false来通知线程池不应该添加新线程。
  2. 第二步,尝试将第一步多余的任务数放入存储队列,如果任务可以成功放入存储队列,那么我们仍然需要仔细检查是否应该添加线程(因为现有线程自上次检查后就死掉了),或者自进入此方法时线程池已关闭。因此,我们重新检查状态,并在必要时回滚排队(如果已停止),或者在没有线程的情况下启动新线程。
  3. 如果我们无法将任务放入存储队列,则尝试添加一个新线程。如果创建新线程失败,则说明该线程池已关闭或已饱和,因此拒绝该提交的任务线程。

reject(Runnable command)

public class ThreadPoolExecutor extends AbstractExecutorService {    private volatile RejectedExecutionHandler handler;    final void reject(Runnable command) {        handler.rejectedExecution(command, this);    }}

rejectedExecution(Runnable command , ThreadPoolExecutor executor)

public interface RejectedExecutionHandler {    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);}

该策略在线程池未停止的情况下,会将存储队列中存储时间最长的任务丢弃,然后重新尝试执行新的非空任务线程,只是使用reject方法来拒绝执行额外的线程。

转载地址:http://ykpiz.baihongyu.com/

你可能感兴趣的文章
MySQL Binlog 日志监听与 Spring 集成实战
查看>>
MySQL binlog三种模式
查看>>
multi-angle cosine and sines
查看>>
Mysql Can't connect to MySQL server
查看>>
mysql case when 乱码_Mysql CASE WHEN 用法
查看>>
Multicast1
查看>>
mysql client library_MySQL数据库之zabbix3.x安装出现“configure: error: Not found mysqlclient library”的解决办法...
查看>>
MySQL Cluster 7.0.36 发布
查看>>
Multimodal Unsupervised Image-to-Image Translation多通道无监督图像翻译
查看>>
MySQL Cluster与MGR集群实战
查看>>
multipart/form-data与application/octet-stream的区别、application/x-www-form-urlencoded
查看>>
mysql cmake 报错,MySQL云服务器应用及cmake报错解决办法
查看>>
Multiple websites on single instance of IIS
查看>>
mysql CONCAT()函数拼接有NULL
查看>>
multiprocessing.Manager 嵌套共享对象不适用于队列
查看>>
multiprocessing.pool.map 和带有两个参数的函数
查看>>
MYSQL CONCAT函数
查看>>
multiprocessing.Pool:map_async 和 imap 有什么区别?
查看>>
MySQL Connector/Net 句柄泄露
查看>>
multiprocessor(中)
查看>>