精华内容
下载资源
问答
  • 四种线程池拒绝策略

    万次阅读 多人点赞 2019-08-08 20:50:38
    但是,线程池的拒绝策略,相信知道的人会少许多。 二、四种线程池拒绝策略 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种...

    一、前言


    线程池,相信很多人都有用过,没用过相信的也有学习过。但是,线程池的拒绝策略,相信知道的人会少许多。


    二、四种线程池拒绝策略


    当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务


    三、线程池默认的拒绝策略


    既然有四种拒绝策略可以选择,那么线程池的默认拒绝策略是什么呢?查看

    java.util.concurrent.ThreadPoolExecutor类的源码,我们可以看到:

    /**
     * The default rejected execution handler
     */
    private static final RejectedExecutionHandler defaultHandler =
     new AbortPolicy();

    线程池的默认拒绝策略为AbortPolicy,即丢弃任务并抛出RejectedExecutionException异常。我们可以通过代码来验证这一点,现有如下代码:

    public class ThreadPoolTest {
    public static void main(String[] args) {
    BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
    ThreadFactory factory = r -> new Thread(r, "test-thread-pool");
    ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
    0L, TimeUnit.SECONDS, queue, factory);
    while (true) {
    executor.submit(() -> {
    try {
    System.out.println(queue.size());
    Thread.sleep(10000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    });
    }
    }
    }

    这里是一个默认的线程池,没有设置拒绝策略,设置了最大线程队列是100。运行代码:


    结果是符合预期的,这也证明了线程池的默认拒绝策略是ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。


    四、设置线程池拒绝策略


    如果我们想要根据实际业务场景需要,设置其他的线程池拒绝策略,可以通过ThreadPoolExecutor重载的构造方法进行设置:


    现在的开发中,相信大家都有使用spring,其实我们也可以通过spring提供的org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor构建线程池。如下:


    @Configuration
    public class TaskExecutorConfig implements AsyncConfigurer {
    /**
    * Set the ThreadPoolExecutor's core pool size.
    */

    private static final int CORE_POOL_SIZE = 5;
    /**
    * Set the ThreadPoolExecutor's maximum pool size.
    */

    private static final int MAX_POOL_SIZE = 5;
    /**
    * Set the capacity for the ThreadPoolExecutor's BlockingQueue.
    */

    private static final int QUEUE_CAPACITY = 1000;
    /**
    * 通过重写getAsyncExecutor方法,制定默认的任务执行由该方法产生
    * <p>
    * 配置类实现AsyncConfigurer接口并重写getAsyncExcutor方法,并返回一个ThreadPoolTaskExevutor
    * 这样我们就获得了一个基于线程池的TaskExecutor
    */

    @Override
    public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
    taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
    taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
    taskExecutor.initialize();
    taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
    return taskExecutor;
    }
    }


    五、拒绝策略场景分析


    (1)AbortPolicy
    AbortPolicy

    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

    A handler for rejected tasks that throws a {@code RejectedExecutionException}.

    这是线程池默认的拒绝策略,在任务不能再提交的时候,抛出异常,及时反馈程序运行状态。如果是比较关键的业务,推荐使用此拒绝策略,这样子在系统不能承载更大的并发量的时候,能够及时的通过异常发现。

    (2)DiscardPolicy

    ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。

    A handler for rejected tasks that silently discards therejected task.
    

    使用此策略,可能会使我们无法发现系统的异常状态。建议是一些无关紧要的业务采用此策略。例如,本人的博客网站统计阅读量就是采用的这种拒绝策略。

    (3)DiscardOldestPolicy

    ThreadPoolExecutor.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.

    此拒绝策略,是一种喜新厌旧的拒绝策略。是否要采用此种拒绝策略,还得根据实际业务是否允许丢弃老任务来认真衡量。

    (4)CallerRunsPolicy

    ThreadPoolExecutor.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 void main(String[] args) {
     BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
     ThreadFactory factory = r -> new Thread(r, "test-thread-pool");
     ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
     0L, TimeUnit.SECONDS, queue, factory, new ThreadPoolExecutor.CallerRunsPolicy());
     for (int i = 0; i < 1000; i++) {
     executor.submit(() -> {
     try {
     System.out.println(Thread.currentThread().getName() + ":执行任务");
     Thread.sleep(1000);
     } catch (InterruptedException e) {
     e.printStackTrace();
     }
     });
     }
    }
    
    

     

     

    把队列最大值改为10,打印输出线程的名称。执行结果如下:

     

     

    通过结果可以看到,主线程main也执行了任务,这正说明了此拒绝策略由调用线程(提交任务的线程)直接执行被丢弃的任务的。

    六、总结


    本文介绍和演示了四种线程池拒绝策略,具体使用哪种策略,还得根据实际业务场景才能做出抉择。

    展开全文
  • 线程池的拒绝策略 ThreadPoolExecutor内部有实现4个拒绝策略,默认为AbortPolicy策略 CallerRunsPolicy:由调用execute方法提交任务的线程来执行这个任务 AbortPolicy:抛出异常RejectedExecutionException拒绝提交...

    线程池的拒绝策略

    ThreadPoolExecutor内部有实现4个拒绝策略,默认为AbortPolicy策略

    • CallerRunsPolicy:由调用execute方法提交任务的线程来执行这个任务
    • AbortPolicy:抛出异常RejectedExecutionException拒绝提交任务
    • DiscardPolicy:直接抛弃任务,不做任何处理
    • DiscardOldestPolicy:去除任务队列中的第一个任务,重新提交

    线程池中,有三个重要的参数,决定影响了拒绝策略:corePoolSize - 核心线程数,也即最小的线程数。workQueue - 阻塞队列 。 maximumPoolSize - 最大线程数

    当提交任务数大于 corePoolSize 的时候,会优先将任务放到 workQueue 阻塞队列中。当阻塞队列饱和后,会扩充线程池中线程数,直到达到 maximumPoolSize 最大线程数配置。此时,再多余的任务,则会触发线程池的拒绝策略了。

    总结起来,也就是一句话,当提交的任务数大于(workQueue.size() + maximumPoolSize ),就会触发线程池的拒绝策略。

    拒绝策略的源码

    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.
         * 用于拒绝任务的处理程序,
         * 可以直接在{@code execute}方法的调用线程中运行被拒绝的任务
         * 除非执行器已被关闭,否则将丢弃该任务。
         */
        public static class CallerRunsPolicy implements RejectedExecutionHandler {
            /**
             * Creates a {@code CallerRunsPolicy}.
             * 创建一个{@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.
             * 除非执行器已关闭,否则在调用者线程中执行任务,
             * r 在这种情况下,该任务将被丢弃。
             *
             * @param r the runnable task requested to be executed
             *          r 请求执行的可运行任务
             * @param e the executor attempting to execute this task
             *          e 尝试执行此任务的执行者
             */
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                if (!e.isShutdown()) {
                    r.run();
                }
            }
        }
    
    分析:

    CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

    这个策略显然不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。(开始我总不想丢弃任务的执行,但是对某些应用场景来讲,很有可能造成当前线程也被阻塞。如果所有线程都是不能执行的,很可能导致程序没法继续跑了。需要视业务情景而定吧。)

    这样生产者虽然没有被阻塞,但提交任务也会被暂停。

    但这种策略也有隐患,当生产者较少时,生产者消费任务的时间里,消费者可能已经把任务都消费完了,队列处于空状态,当生产者执行完任务后才能再继续生产任务,这个过程中可能导致消费者线程的饥饿。

    AbortPolicy

        /**
         * A handler for rejected tasks that throws a
         * {@code RejectedExecutionException}.
         * 抛出{@code RejectedExecutionException}的拒绝任务处理程序。
         */
        public static class AbortPolicy implements RejectedExecutionHandler {
            /**
             * Creates an {@code AbortPolicy}.
             */
            public AbortPolicy() { }
    
            /**
             * Always throws RejectedExecutionException.
             * 总是抛出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());
            }
        }
    
    分析:

    该策略是默认饱和策略。

    使用该策略时在饱和时会抛出RejectedExecutionException(继承自RuntimeException),调用者可捕获该异常自行处理。

    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.
             * 不执行任何操作,这具有丢弃任务 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.
         * 处理被拒绝任务的处理程序,它丢弃最旧的未处理请求,
         * 然后重试{@code execute},
         * 除非执行器*被关闭,在这种情况下,该任务将被丢弃。
         */
        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.
             * 获取并忽略执行者*会立即执行的下一个任务(如果一个任务立即可用),
             * 然后重试任务r的执行,除非执行者*被关闭,在这种情况下,任务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) {
                if (!e.isShutdown()) {
                    e.getQueue().poll();
                    e.execute(r);
                }
            }
        }
    
    分析:

    如代码,先将阻塞队列中的头元素出队抛弃,再尝试提交任务。如果此时阻塞队列使用PriorityBlockingQueue优先级队列,将会导致优先级最高的任务被抛弃,因此不建议将该种策略配合优先级队列使用。

    自定义策略

    看完发现默认的几个拒绝策略并不是特别的友好,那么可不可以咱们自己搞个呢?

    可以发现,所有的拒绝策略都是实现了 RejectedExecutionHandler 接口

    public interface RejectedExecutionHandler {
    
        /**
         * Method that may be invoked by a {@link ThreadPoolExecutor} when
         * {@link ThreadPoolExecutor#execute execute} cannot accept a
         * task.  This may occur when no more threads or queue slots are
         * available because their bounds would be exceeded, or upon
         * shutdown of the Executor.
         *
         * <p>In the absence of other alternatives, the method may throw
         * an unchecked {@link RejectedExecutionException}, which will be
         * propagated to the caller of {@code execute}.
         *
         * @param r the runnable task requested to be executed
         * @param executor the executor attempting to execute this task
         * @throws RejectedExecutionException if there is no remedy
         */
        void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
    }
    
    

    这个接口只有一个 rejectedExecution 方法。

    r 为待执行任务;executor 为线程池;方法可能会抛出拒绝异常。

    那么咱们就可以通过实现 RejectedExecutionHandler 接口扩展

    两个栗子:一

    netty自己实现的线程池里面私有的一个拒绝策略。单独启动一个新的临时线程来执行任务。

        private static final class NewThreadRunsPolicy implements RejectedExecutionHandler {
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                try {
                    final Thread t = new Thread(r, "Temporary task executor");
                    t.start();
                } catch (Throwable e) {
                    throw new RejectedExecutionException(
                            "Failed to start a new thread", e);
                }
            }
        }
    

    两个栗子:二

    dubbo的一个例子,它直接继承的 AbortPolicy ,加强了日志输出,并且输出dump文件

    public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            String msg = String.format("Thread pool is EXHAUSTED!" +
                            " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," +
                            " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!",
                    threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),
                    e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(),
                    url.getProtocol(), url.getIp(), url.getPort());
            logger.warn(msg);
            dumpJStack();
            throw new RejectedExecutionException(msg);
        }
    }
    

    自己玩

    参考类似的思路,最简单的做法,我们可以直接定义一个RejectedExecutionHandler,当队列满时改为调用BlockingQueue.put来实现生产者的阻塞:

        new RejectedExecutionHandler() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                    if (!executor.isShutdown()) {
                        try {
                            executor.getQueue().put(r);
                        } catch (InterruptedException e) {
                            // should not be interrupted
                        }
                    }
                }
            };
    

    这样,我们就无需再关心Queue和Consumer的逻辑,只要把精力集中在生产者和消费者线程的实现逻辑上,只管往线程池提交任务就行了。

    相比最初的设计,这种方式的代码量能减少不少,而且能避免并发环境的很多问题。当然,你也可以采用另外的手段,例如在提交时采用信号量做入口限制等,但是如果仅仅是要让生产者阻塞,那就显得复杂了。

    总结

    四种线程池拒绝策略,具体使用哪种策略,还得根据实际业务场景才能做出抉择。

    展开全文
  • 线程池拒绝策略

    千次阅读 2018-10-12 20:51:31
    今天看了线程池的拒绝策略: 我们看下ThreadPoolExecutor的API:看它的构造方法: 最后一个参数就是今天的主角:拒绝策略 首先点开这个类 我们发现他是一个接口,点开它的实现类,我们发现这些实现类就是...

    今天看了线程池的拒绝策略:

    我们看下ThreadPoolExecutor的API:看它的构造方法:

    最后一个参数就是今天的主角:拒绝策略

    首先点开这个类

    我们发现他是一个接口,点开它的实现类,我们发现这些实现类就是我们所说的各种任务拒绝策略

    我们一个一个分析

    首先我们发现构造方法里有不需要拒绝策略的构造方法。那么一定就有一个默认的构造方法我们从这个讲起:

    这就是我们的默认拒绝策略:它的拒绝方式是直接抛出异常。简单粗暴

    试验:

    我们自定义一个线程池:核心线程池大小为1,最大线程池大小为1,设置保留线程的时间为0,使用SynchronousQueue队列实现(这个队列是缓冲线程池中的队列,我们只需要知道在这里它拒绝接受线程就好了)

    源码:

    思路:当我们接受一个线程让他沉睡数秒,此时另一个线程进来,我们看看会发生什么

    代码如下:

    import java.util.concurrent.*;
    
    class Thread1 extends Thread {
        public void run() {
            try {
                Thread.sleep(1000);//线程1沉睡数秒,保证线程2进入线程池时,线程1还在运行中
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threada");
        }
    }
    
    
    class Thread2 extends Thread {
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadb");
        }
    
    }
    
    public class Main {
        public static void main(String[] args) throws InterruptedException {
            Thread1 thread1 = new Thread1();
            Thread2 thread2 = new Thread2();
    
            ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0,
                    TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
            executorService.execute(thread1);//执行线程1
    
            executorService.execute(thread2);//线程1沉睡时线程2来了
    
            executorService.shutdown();//关闭
    
        }
    }

     

    执行结果:

    抛出异常首先,然后1线程结束了沉睡完成输出

    总结:这个拒绝策略就是当线程池满员,我们就直接抛出异常

     

    2.再看一个

    这个拒绝策略也很简单,它的意思就是,假如现在使用到了拒绝策略,那么我就将这些被拒绝的线程用当前调用它们的线程执行这些任务

    如:线程2被拒绝,是main线程调用这个线程开启的任务,因此我们直接去使用main的线程执行这些被拒绝的任务,如果执行线程池关闭了,那么就放弃这个任务

    源码:

    试验:

    和上面的参数相同,只是改变了执行策略:代码如下:

    import java.util.concurrent.*;
    
    class Thread1 extends Thread {
        public void run() {
            try {
                Thread.sleep(1000);//线程1沉睡数秒,保证线程2进入线程池时,线程1还在运行中
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threada");
        }
    }
    
    
    class Thread2 extends Thread {
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadb");
        }
    
    }
    
    public class Main {
        public static void main(String[] args) throws InterruptedException {
            Thread1 thread1 = new Thread1();
            Thread2 thread2 = new Thread2();
            ExecutorService executorService= new ThreadPoolExecutor(1,1,0,
            TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),new 
            ThreadPoolExecutor.CallerRunsPolicy());
            executorService.execute(thread1);//执行线程1
            executorService.execute(thread2);//线程1沉睡时线程2来了
            executorService.shutdown();//关闭
    
        }
    }
    

     执行结果如下:

    我们发现时用过了main线程执行了b任务,和上面说的一样

     

    再看第三个:

     当我们达到了执行拒绝策略的条件时,我们将任务队列中的最早的任务,也就是队列头移除,这时候再一次执行这个execute操作,这时没有其他因素影响的话我们就进了队列,等待执行了

    源码:

    测试如下:

    我们设置三个线程,线程1先开启进入睡眠,然后线程2进入队列,此时再让线程3进来,我们的的预期结果是,把线程2踢出队列,线程3进入队列,线程1沉睡完后就执行线程3,线程2此时就被抛弃了:

    import java.util.concurrent.*;
    
    class Thread1 extends Thread {
        public void run() {
            try {
                Thread.sleep(1000);//线程1沉睡数秒,保证线程2进入线程池时,线程1还在运行中
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threada");
        }
    }
    
    
    class Thread2 extends Thread {
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadb");
        }
    }
    
    class Thread3 extends Thread {
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadc");
        }
    }
    
    public class Main {
        public static void main(String[] args) throws InterruptedException {
            Thread1 thread1 = new Thread1();
            Thread2 thread2 = new Thread2();
            Thread3 thread3 = new Thread3();
            ExecutorService executorService= new ThreadPoolExecutor(1,1,0,
            TimeUnit.SECONDS,  new LinkedBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardOldestPolicy());
            executorService.execute(thread1);//执行线程1
            executorService.execute(thread2);//线程1沉睡时线程2来了,进入了工作队列等待
            executorService.execute(thread3);//线程1沉睡时线程3来了,此时线程2位于队列中,队列满了,我们执行拒绝策略
            executorService.shutdown();//关闭
    
        }
    }

    执行结果:

    先停顿了一下,然后输出上面的结果,我们发现并没有线程2执行

     

    第四种:

    这个很简单其实就是直接丢弃并且不抛出异常:

    源码如下:

    测试:

    我们还是先让a进入后睡眠,此时b进来到了队列,c也进来,但是c该被拒绝通过拒绝策略,所以直接丢弃

    代码如下:

     

    import java.util.concurrent.*;
    
    class Thread1 extends Thread {
        public void run() {
            try {
                Thread.sleep(1000);//线程1沉睡数秒,保证线程2进入线程池时,线程1还在运行中
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threada");
        }
    }
    
    
    class Thread2 extends Thread {
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadb");
        }
    }
    
    class Thread3 extends Thread {
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadc");
        }
    }
    
    public class Main {
        public static void main(String[] args) throws InterruptedException {
            Thread1 thread1 = new Thread1();
            Thread2 thread2 = new Thread2();
            Thread3 thread3 = new Thread3();
            ExecutorService executorService= new ThreadPoolExecutor(1,1,0,
            TimeUnit.SECONDS,  new LinkedBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardPolicy());
            executorService.execute(thread1);//执行线程1
            executorService.execute(thread2);//线程1沉睡时线程2来了,进入了工作队列等待
            executorService.execute(thread3);//线程1沉睡时线程3来了,此时线程2位于队列中,队列满了,我们执行拒绝策略,放弃线程3
            executorService.shutdown();//关闭
    
        }
    }

    执行结果如下:

    只输出了a和b

     

    pis:我们看了拒绝策略的源码,发现其实,当用到拒绝策略时我们都会调用拒绝类里的rejectedExecution方法方法

    展开全文
  • 主要介绍了Java线程池的拒绝策略实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 重温线程池拒绝策略

    2021-08-13 18:02:16
    ThreadPoolExecutor的构造方法的最后一个参数指定了拒绝策略, 当提交给线程池的任务量超过实际承载能力时,如何处理? 即线程池中的线程已经用完,等待队列也满了,无法为新提交的任务服务,可以通过拒绝策略来处理...

    ThreadPoolExecutor的构造方法的最后一个参数指定了拒绝策略,
    当提交给线程池的任务量超过实际承载能力时,如何处理?
    即线程池中的线程已经用完,等待队列也满了,无法为新提交的任务服务,可以通过拒绝策略来处理这个问题。

    • 四种拒绝策略:

                   1.AbortPolicy策略:会抛出异常
                   2.CallerRunPolicy策略:只要线程池没关闭,会在调用者线程中运行当前被丢弃的任务
                   3.DiscardOldestPolicy策略:任务队列中最老的任务丢弃,尝试再次提交新任务
                   4.DiscardPolicy策略:直接丢弃无法处理的任务
        
    Executor工具类提供的静态方法返回的线程池默认的拒绝策略是AbortPolicy策略,抛出异常
    如果内置的策略无法满足实际需求,可以扩展RejectedExecutionHandler接口

    1.AbortPolicy策略

        public static class AbortPolicy implements RejectedExecutionHandler {
    
            public AbortPolicy() { }
    
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) 
            {
                throw new RejectedExecutionException("Task " + r.toString() +
                                                     " rejected from " +
                                                     e.toString());
            } 
        }

    2.CallerRunPolicy策略

        public static class CallerRunsPolicy implements RejectedExecutionHandler {
    
            public CallerRunsPolicy() { }
    
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                if (!e.isShutdown()) { //如果线程池没有关闭,则将当前任务尝试执行一次
                    r.run();
                }
            }
        }

    3.DiscardOldestPolicy策略

       public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    
            public DiscardOldestPolicy() { }
    
    
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                if (!e.isShutdown()) { 
                    e.getQueue().poll(); //弹出并移除队头中的一个任务并执行当前的任务
                    e.execute(r);
                }
            }
        }

    4.DiscardPolicy策略

       public static class DiscardPolicy implements RejectedExecutionHandler {
    
            public DiscardPolicy() { }
    
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
            } //方法为空,什么都不做即为:任务丢弃
        }

    个人实现拒绝策略,只需要实现借口: implements RejectedExecutionHandler 就可以了。

    在jdk默认实现中使用的是拒绝异常策略:

     private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

    可以看到在定义的默认策略中使用的是AbortPolicy 即为直接抛出异常:

    RejectedExecutionException

    展开全文
  • 线程池自定义拒绝策略

    千次阅读 2020-04-22 20:24:28
    通过实现RejectedExecutionHandler接口,自定义一个拒绝策略类,重写它的rejectedExecution()方法: public class CustomRejectionHandler implements RejectedExecutionHandler { @Override public void ...
  • 17.拒绝策略

    千次阅读 2018-04-03 08:42:14
    ThreadPoolExecutor的最后一个参数指定了拒绝策略,也就是当线程池中的线程已经使用完了,无法为新的任务服务,同时等待队列中也已经排满了,再也无法塞下新任务了。拒绝策略就是用来处理这个情况的。 JDK内置了四...
  • 线程池的4种拒绝策略

    千次阅读 2020-06-25 05:48:41
    线程池的4种拒绝策略 文章目录线程池的4种拒绝策略1.拒绝时机2.拒绝策略2.1 核心接口2.2 内建实现2.2.1 CallerRunsPolicy2.2.2 AbortPolicy2.2.3 DiscardPolicy2.2.4 DiscardOldestPolicy3.参考 1.拒绝时机 线程池会...
  • Java线程池的8大拒绝策略

    千次阅读 2019-11-21 20:19:49
    拒绝策略接口定义 CallerRunsPolicy AbortPolicy DiscardPolicy DiscardOldestPolicy dubbo中的线程拒绝策略 Netty中的线程池拒绝策略 activeMq中的线程池拒绝策略 pinpoint中的线程池拒绝策略
  • 线程池的拒绝策略

    2020-09-20 21:58:42
    线程池的拒绝策略 1、是什么? 等待队列已经排满了,再也塞不下新任务了 同时,线程池中的max线程也达到了,无法继续为新任务服务。 这个是时候我们就需要拒绝策略机制合理的处理这个问题。 2、JDK内置的拒绝策略 ...
  • Java自定义拒绝策略与线程池

    千次阅读 2017-07-01 12:25:42
    1、拒绝策略ThreadPoolExcetor 的最后一个参数指定了拒绝策略,JDK提供了四种拒绝策略: AbortPolicy 策略、CallerRunsPolicy策略、 DiscardOledestPolicy策略、DiscardPolicy策略。
  • 线程池拒绝策略详解

    千次阅读 2019-10-16 09:55:11
    线程池拒绝策略详解 JDK中已经预设了4种线程池拒绝策略,下面结合场景详细聊聊这些策略的使用场景,以及我们还能扩展哪些拒绝策略。 池化设计思想 池话设计应该不是一个新名词。我们常见的如java线程池、jdbc连接...
  • 线程池中的拒绝策略一、拒绝策略产生缘由?二、何时触发使用拒绝策略三、拒绝策略都有哪些?1、AbortPolicy策略2、CallerRunsPolicy策略3、DiscardOldestPolicy策略4、DiscardPolicy策略自定义拒绝策略线程池使用...
  • Java线程池的拒绝策略

    万次阅读 2018-08-08 17:05:33
    拒绝策略RejectedExecutionHandler做一下详细的工作:在使用线程池并且使用有界队列的时候,如果队列满了,任务添加到线程池的时候就会有问题,针对这些问题java线程池提供了以下几种策略: (1)AbortPolicy (2)...
  • 【线程池】四种拒绝策略

    千次阅读 2019-09-18 23:21:12
    当我们初始化线程池时 ThreadPoolExecutor, 其中包含参数 RejectedExecutionHandler 拒绝策略。 当线程池中 线程数量等于最大线程数量 且 任务队列已满时,则会执行线程池会对新增加的任务执行拒绝策略。 ...
  • Java线程池-自定义拒绝策略

    千次阅读 2018-06-11 17:53:31
    Q: 什么时候需要使用拒绝策略呢? A: 当任务数量超过系统实际承载能力的时候就要用到拒绝策略了,可以说它是系统超负荷运行的补救措施。简言之,就是线程用完,队列已满,无法为新任务服务,则需一套机制来合理的...
  • 线程池的四种拒绝策略

    千次阅读 2019-08-21 16:27:27
    今天写线程池的时候由于设定了最大线程数,需要写拒绝策略,顺便就把4中拒绝策略记录一下! 1.AbortPolicy:ThreadPoolExecutor中默认的拒绝策略就是AbortPolicy。直接抛出异常也不处理 private static final ...
  • 线程池拒绝策略——CallerRunsPolicy

    千次阅读 2020-06-30 21:44:44
    最近在搞线程池,用到了CallerRunsPolicy这个拒绝策略。 为什么选用CallerRunsPolicy? 因为考虑到数据一条都不能丢失,所以选择了CallerRunsPolicy策略。保证数据来了不会丢。天真的幻想着这策略好啊! 经过了一段...
  • 很多人都知道或者用过线程池,线程池构造方法的参数中有一个参数为拒绝策略。 那么,通过拒绝策略我们可以学到哪些思想? 本文简单讲讲自己的理解。 二、可以学到什么? 2.1 没有最好的选择,只有最适合的选择 不知道...
  • 多线程之四大拒绝策略

    千次阅读 2017-10-23 15:00:34
    定义拒绝策略,需要重写RejectedExecutionHandler接口的rejectedExecution(Runnable r, ThreadPoolExecutor executor);方法。public interface RejectedExecutionHandler { void rejectedExecution(Runnable r, ...
  • java线程池的四种拒绝策略 ThreadPoolExcutor中有四个内部类实现了线程池的拒绝策略。当然我们也可以自己定义,这里讲解一下这四个已经实现的拒绝策略。 什么情况下会拒绝新的任务 在什么情况下,线程池会拒绝新提交...
  • 线程池之拒绝策略

    万次阅读 2016-05-13 11:21:05
    ThreadPoolExecutor.execute(Runnable command)提供了提交任务的入口,此方法会自动判断如果池子满了的话,则会调用拒绝策略来执行此任务,接口为RejectedExecutionHandler,内置的4中策略分别为AbortPolicy(默认)...
  • 线程池学习之线程池任务拒绝策略

    千次阅读 2019-05-23 09:40:07
    对于线程池的任务拒绝策略没有过多的介绍,本文主要介绍线程的四种拒绝策略。 RejectedExecutionHandler提供了多种方式来处理任务拒绝策略 通过观察源码可知:所有的拒绝策略他们都实现了RejectedExecutionHandler...
  • 英语商务信函的委婉拒绝策略及语用功能修改稿.doc
  • 开篇讲一下,为什么要写线程池饱和后的拒绝策略。早在上个月,就要说写一篇除 java.util.concurrent包之外的四种线程池拒绝策略,开源框架如:ActiveMQ,Dubbo,PinPoint,Netty也都实现了符合自己业务的拒绝策略。 ...
  • Java多线程-任务拒绝策略

    千次阅读 2021-04-07 19:14:25
    本文通过解读 java 官方英文注解及源代码 以帮助读者了解 Java多线程开发中的 任务拒绝策略

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 156,612
精华内容 62,644
关键字:

拒绝策略