如何使用裸ThreadPoolExecutor获取MoreExecutors.newDirectExecutorService()行为?

时间:2017-10-05 21:05:53

标签: guava executorservice

当我运行以下代码时:

package foo.trials;

import com.google.common.util.concurrent.MoreExecutors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class DirectExecutorService {
    private static final Logger logger_ = LoggerFactory.getLogger(DirectExecutoService.class);

    public static void main(String[] args) {
        boolean useGuava = true;

        final ExecutorService directExecutorService;
        if (useGuava) {
            directExecutorService = MoreExecutors.newDirectExecutorService();
        } else {
            directExecutorService = new ThreadPoolExecutor(
                    0, 1, 0, TimeUnit.DAYS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadPoolExecutor.CallerRunsPolicy());
            directExecutorService.submit(new BlockingCallable());
        }

        Future<Boolean> future = directExecutorService.submit(new MyCallable());
        try {
            logger_.info("Result: {}", future.get());
        } catch (InterruptedException e) {
            logger_.error("Unexpected: Interrupted!", e);
        } catch (ExecutionException e) {
            logger_.error("Unexpected: Execution exception!", e);
        }
        logger_.info("Exiting...");
    }

    static class MyCallable implements Callable<Boolean> {
        static final Random _random = new Random();
        @Override
        public Boolean call() throws Exception {
            logger_.info("In call()");
            return _random.nextBoolean();
        }
    }

    static class BlockingCallable implements Callable<Boolean> {

        Semaphore semaphore = new Semaphore(0);
        @Override
        public Boolean call() throws Exception {
            semaphore.acquire(); // this will never succeed.
            return true;
        }
    }
}

我得到以下输出

13:36:55.960 [main] INFO  a.t.DirectExecutoService - In call()
13:36:55.962 [main] INFO  a.t.DirectExecutoService - Result: true
13:36:55.963 [main] INFO  a.t.DirectExecutoService - Exiting...

请注意,所有执行都发生在main线程中。特别是在调用线程中调用了callable的调用get。当然,这就是人们对MoreExecutors.newDirectExecutorService()的期望并不奇怪。

当我将变量useGuava设置为false时,我得到了类似的结果。

13:45:14.264 [main] INFO  a.t.DirectExecutoService - In call()
13:45:14.267 [main] INFO  a.t.DirectExecutoService - Result: true
13:45:14.268 [main] INFO  a.t.DirectExecutoService - Exiting...

如果我注释掉以下行

directExecutorService.submit(new BlockingCallable());

然后我得到以下输出。

13:37:27.355 [pool-1-thread-1] INFO  a.t.DirectExecutoService - In call()
13:37:27.357 [main] INFO  a.t.DirectExecutoService - Result: false
13:37:27.358 [main] INFO  a.t.DirectExecutoService - Exiting...

正如可以看到,callable的调用发生在另一个线程pool-1-thread-1中。我想我可以解释为什么会这样;也许是因为线程池可以有(最多)1个可用的线程,所以第一个Callable被调度到另外的线程,否则被BlockingCallable使用。

我的问题是如何创建一个ExecutorService来执行DirectExecutorService所做的事情而不必人为地烧掉一个永远不会完成的可调用线程?

我为什么要问这个?

  1. 我有一个在11.0版本上使用guava的代码库。我需要避免将其升级到17.0+ - 提供MoreExecutors.newDirectExecutorService() - 如果可以的话。
  2. ThreadPoolExecutor不允许将maxThreads设置为0.如果允许的话会很奇怪,但如果确实如此,那么这也解决了我的问题。
  3. 最后,我很惊讶地注意到这种行为 - 我曾(错误地)假设使用CallerRunsPolicy会导致所有call 全部 Callable s在调用者的线程中执行。所以,我想把我的经验和黑客放在那里,所以其他人可以节省最后燃烧的时间,试图理解这一点。 :(
  4. 如果无法升级到番石榴17.0 +,是否有更好/更惯用的方式来实现DirectExecutorService行为?

1 个答案:

答案 0 :(得分:2)

  

如果无法升级到番石榴17.0 +,是否有更好/更惯用的方式来实现DirectExecutorService行为?

如果这是您唯一的问题,则应使用MoreExecutors.sameThreadExecutor()。在将其移至新方法之前基本为newDirectExecutorService()(并添加了directExecutor()),请参阅Javadoc

  

自:18.0(自10.0以来呈现为MoreExecutors.sameThreadExecutor()

BTW:你应该真的升级到最新的番石榴,你使用的是近六岁的番石榴!