Java中有界队列的饱和策略(reject policy)原理解析

 更新时间:2020年04月24日 10:26:52   作者:flydean程序那些事  
这篇文章主要介绍了Java中有界队列的饱和策略(reject policy)原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

我们在使用ExecutorService的时候知道,在ExecutorService中有个一个Queue来保存提交的任务,通过不同的构造函数,我们可以创建无界的队列(ExecutorService.newCachedThreadPool)和有界的队列(ExecutorService newFixedThreadPool(int nThreads))。

无界队列很好理解,我们可以无限制的向ExecutorService提交任务。那么对于有界队列来说,如果队列满了该怎么处理呢?

今天我们要介绍一下java中ExecutorService的饱和策略(reject policy)。

以ExecutorService的具体实现ThreadPoolExecutor来说,它定义了4种饱和策略。分别是AbortPolicy,DiscardPolicy,DiscardOldestPolicy和CallerRunsPolicy。

如果要在ThreadPoolExecutor中设定饱和策略可以调用setRejectedExecutionHandler方法,如下所示:

    ThreadPoolExecutor threadPoolExecutor= new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(20));
    threadPoolExecutor.setRejectedExecutionHandler(
        new ThreadPoolExecutor.AbortPolicy()
    );

上面的例子中我们定义了一个初始5个,最大10个工作线程的Thread Pool,并且定义其中的Queue的容量是20。如果提交的任务超出了容量,则会使用AbortPolicy策略。

AbortPolicy

AbortPolicy意思是如果队列满了,最新的提交任务将会被拒绝,并抛出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方法中我们直接抛出了RejectedExecutionException异常。

DiscardPolicy

DiscardPolicy将会悄悄的丢弃提交的任务,而不报任何异常。

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

DiscardOldestPolicy将会丢弃最老的任务,保存最新插入的任务。

  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);
      }
    }
  }

我们看到在rejectedExecution方法中,poll了最老的一个任务,然后使用ThreadPoolExecutor提交了一个最新的任务。

CallerRunsPolicy

CallerRunsPolicy和其他的几个策略不同,它既不会抛弃任务,也不会抛出异常,而是将任务回退给调用者,使用调用者的线程来执行任务,从而降低调用者的调用速度。我们看下是怎么实现的:

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();
      }
    }
  }

在rejectedExecution方法中,直接调用了 r.run()方法,这会导致该方法直接在调用者的主线程中执行,而不是在线程池中执行。从而导致主线程在该任务执行结束之前不能提交任何任务。从而有效的阻止了任务的提交。

使用Semaphore

如果我们并没有定义饱和策略,那么有没有什么方法来控制任务的提交速度呢?考虑下之前我们讲到的Semaphore,我们可以指定一定的资源信号量来控制任务的提交,如下所示:

public class SemaphoreUsage {

  private final Executor executor;
  private final Semaphore semaphore;

  public SemaphoreUsage(Executor executor, int count) {
    this.executor = executor;
    this.semaphore = new Semaphore(count);
  }

  public void submitTask(final Runnable command) throws InterruptedException {
    semaphore.acquire();
    try {
      executor.execute(() -> {
            try {
              command.run();
            } finally {
              semaphore.release();
            }
          }
      );
    } catch (RejectedExecutionException e) {
      semaphore.release();
    }
  }
}

本文的例子可参考https://github.com/ddean2009/learn-java-concurrency/tree/master/rejectPolicy

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java关键字volatile和synchronized作用和区别

    Java关键字volatile和synchronized作用和区别

    这篇文章主要为大家详细介绍了Java关键字volatile和synchronized的作用和区别,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • 浅谈Java中几个常用集合添加元素的效率

    浅谈Java中几个常用集合添加元素的效率

    下面小编就为大家带来一篇浅谈Java中几个常用集合添加元素的效率。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • java实现俄罗斯方块

    java实现俄罗斯方块

    这篇文章主要为大家详细介绍了java实现俄罗斯方块,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • ES模糊查询失效的坑以及解决方案

    ES模糊查询失效的坑以及解决方案

    ES的查询原理是按分词建立索引,根据要保存的内容先分词,然后按照分词的结果建立索引,这篇文章主要给大家介绍了关于ES模糊查询失效的坑及解决方案的相关资料,需要的朋友可以参考下
    2023-09-09
  • Spring容器刷新prepareRefresh第一步

    Spring容器刷新prepareRefresh第一步

    这篇文章主要为大家介绍了Spring容器刷新prepareRefresh第一步示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • java token生成和校验的实例代码

    java token生成和校验的实例代码

    这篇文章主要介绍了java token生成和校验的实例代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • Java集合使用 Iterator 删除元素

    Java集合使用 Iterator 删除元素

    这篇文章主要介绍了Java集合使用 Iterator 删除元素,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • java使用dom4j解析xml配置文件实现抽象工厂反射示例

    java使用dom4j解析xml配置文件实现抽象工厂反射示例

    本文主要介绍了java使用dom4j读取配置文件实现抽象工厂和反射的示例,在Java中也可以同Donet一样,将差异配置在配置文件里面。另外,我们采用下面的方式实现,将会更加便捷
    2014-01-01
  • JVM类加载之双亲委派机制解读

    JVM类加载之双亲委派机制解读

    这篇文章主要介绍了JVM类加载之双亲委派机制解读,类加载阶段分为加载、连接、初始化三个阶段,而加载阶段需要通过类的全限定名来获取定义了此类的二进制字节流,Java特意把这一步抽出来用类加载器来实现,需要的朋友可以参考下
    2023-12-12
  • Java11中的新增字符串APIs使用实例探究

    Java11中的新增字符串APIs使用实例探究

    这篇文章主要为大家介绍了Java11中的新增字符串APIs使用实例探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01

最新评论