【问题标题】:Unit test for throwing RejectedExecutionException by Java thread poolJava线程池抛出RejectedExecutionException的单元测试
【发布时间】:2017-01-02 01:52:15
【问题描述】:

我是使用 Java 线程池的新手。现在我有一个单元测试的用例,当一个新任务到来并且当前线程数是最大数并且队列已满时。我知道在这种情况下RejectedExecutionException 会被抛出。但是如何最好地产生这个场景,我现在能想到的是这样的:

LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(1);
ExecutorService ex = new ThreadPoolExecutor(2, 2, 60L, TimeUnit.MILLISECONDS, queue);
int rejected = 0;
try{
    while (true){
        ex.submit(() -> {
            System.out.println("Queue remaining size: " + queue.remainingCapacity());
            System.out.println("Thread amount in pool: " + ((ThreadPoolExecutor)ex).getPoolSize() + "\n");
        });
    }
}
catch (RejectedExecutionException e){
    System.out.println(++rejected);
}

这是基本想法,如果方法正确,我需要使用 EasyMock 进行转换。我想知道如果使用 EasyMock 而不是继续提交任务直到线程和队列已满,是否有更好的方法。

【问题讨论】:

    标签: java multithreading unit-testing


    【解决方案1】:

    这是基本想法,如果方法正确,我需要使用 EasyMock 进行转换。我想知道如果使用 EasyMock 而不是一直提交任务直到线程和队列已满,是否有更好的方法。

    EasyMock 所做的任何事情很可能会比您的匿名Runnable 花费更多的代码。你当然可以这样做,但你会使用 IAnswer 对象而不是 Runnable,所以我不确定它是否更好。

    真正的问题是你在测试什么?您是否真的确保ThreadPoolExecutor 正在完成它的工作,或者您是否真的想模仿ExecutorService 或@Warren 提到的ThreadPoolExecutor

    我会在您的 Runnable 中创建一个 Thread.sleep(1000) 并提交其中的 4 个,而不是 while 循环。您也可以在开始时创建一个对象并多次提交。请原谅 Java 7 代码:

    BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(1);
    ExecutorService ex = new ThreadPoolExecutor(2, 2, 60L, TimeUnit.MILLISECONDS, queue);
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
        }
    };
    try {
        ex.submit(runnable); // 1st thread
        ex.submit(runnable); // 2nd thread
        ex.submit(runnable); // 1st queued
        ex.submit(runnable); // rejected
    } catch (RejectedExecutionException e) {
        System.out.println("rejected");
    }
    

    【讨论】:

      【解决方案2】:

      我对 EasyMock 不是特别熟悉,但一般的方法应该是模拟出 ThreadPoolExecutor。

      【讨论】:

      • 我支持这一点,这样做的方法是删除 ThreadPoolExecutorLinkedBlockingQueue 的实例化,它依赖于类外并注入它(最好作为构造函数参数)。然后很容易用模拟替换ThreadPoolExecutor。由于ThreadPoolExecutor 正在实现接口Executor,因此构造函数参数应该是该接口类型。
      【解决方案3】:

      一个简单的解决方案:

      在提交任务之前致电shutdown

      如果你在shutdown(之后调用submit()),你会得到这种类型的执行:

      java.util.concurrent.RejectedExecutionException: Task MyRunnable@3dcc0a0f
      rejected from java.util.concurrent.ThreadPoolExecutor
      

      【讨论】:

        【解决方案4】:

        您可以将执行代码移动到一个新类中,您可以在其中轻松地模拟出依赖项:

        class MyExecuter extends Executer{
          private final ExecuterService executerService;
          private final PrintStream out;
          MyExecuter(ExecutorService executerService, PrintStream out){
            this.executerService  =  executerService;
            this.out  =  out;
          }
          @override 
           public void execute(Runnable r) {
             int rejected = 0;
             try{
                while (true)
                {
                    executerService.submit(() -> {
                       // do your business logic which usually does not care about the executor or the queue...
                    });
                }
            }
            catch (RejectedExecutionException e)
            {
                out.println(++rejected);
            }
        
           }
        }
        

        你的代码会变成这样:

        LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(1);
        ExecutorService ex = new ThreadPoolExecutor(2, 2, 60L, TimeUnit.MILLISECONDS, queue);
        Executor mex = new MyExcecutor(ex, System.out);
        mex.execute();
        

        现在您可以为 MyExcecutor 创建一个测试,该测试获取 ExecutorServicePrintStream 的模拟,以便您可以验证与它们的交互。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多