如何使用裸 ThreadPoolExecutor 获得 MoreExecutors.newDirectExecutorService() 行为?
How to get MoreExecutors.newDirectExecutorService() behavior by using bare ThreadPoolExecutor?
当我运行以下代码时:
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
线程中。特别是在调用线程中调度可调用对象的调用。当然,这是人们对 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...
正如你所看到的,可调用对象的调用发生在另一个线程中 pool-1-thread-1
。我想我可以解释为什么会这样;也许是因为线程池可以有(最多)1 个可用线程,所以第一个 Callable 被分派到那个额外的线程,否则该线程被 BlockingCallable
.
消耗
我的问题是如何创建一个 ExecutorService
来完成 DirectExecutorService
所做的事情,而不必人为地使用永远不会完成的可调用线程来燃烧线程?
我为什么要问这个?
- 我有一个使用 11.0 版番石榴的代码库。我需要避免将其升级到 17.0+——它提供
MoreExecutors.newDirectExecutorService()
——如果可以的话。
ThreadPoolExecutor
不允许将 maxThreads
设置为 0。如果它允许这样做会很奇怪,但如果它允许的话那也解决了我的问题。
- 最后,我很惊讶地注意到这种行为——我曾(错误地)假设使用
CallerRunsPolicy
会导致 all 的所有 call
Callable
s 将在调用者的线程中执行。所以,我想把我的经验和 hack 放在那里,这样其他人就可以节省最终在试图理解这一点时耗费的时间。 :(
如果无法升级到 Guava 17.0+,是否有 better/more 惯用的方法来实现类似 DirectExecutorService 的行为?
Is there better/more idiomatic way to achieve DirectExecutorService like behavior if one can't upgrade to guava 17.0+?
如果这是你唯一的问题,你应该使用 MoreExecutors.sameThreadExecutor()
. It's basically newDirectExecutorService()
before it was moved to a new method (and directExecutor()
was added), see Javadoc:
Since: 18.0 (present as MoreExecutors.sameThreadExecutor()
since 10.0)
顺便说一句:你真的应该升级到最新的番石榴,你用的是快六岁的番石榴了!
当我运行以下代码时:
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
线程中。特别是在调用线程中调度可调用对象的调用。当然,这是人们对 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...
正如你所看到的,可调用对象的调用发生在另一个线程中 pool-1-thread-1
。我想我可以解释为什么会这样;也许是因为线程池可以有(最多)1 个可用线程,所以第一个 Callable 被分派到那个额外的线程,否则该线程被 BlockingCallable
.
我的问题是如何创建一个 ExecutorService
来完成 DirectExecutorService
所做的事情,而不必人为地使用永远不会完成的可调用线程来燃烧线程?
我为什么要问这个?
- 我有一个使用 11.0 版番石榴的代码库。我需要避免将其升级到 17.0+——它提供
MoreExecutors.newDirectExecutorService()
——如果可以的话。 ThreadPoolExecutor
不允许将maxThreads
设置为 0。如果它允许这样做会很奇怪,但如果它允许的话那也解决了我的问题。- 最后,我很惊讶地注意到这种行为——我曾(错误地)假设使用
CallerRunsPolicy
会导致 all 的所有call
Callable
s 将在调用者的线程中执行。所以,我想把我的经验和 hack 放在那里,这样其他人就可以节省最终在试图理解这一点时耗费的时间。 :(
如果无法升级到 Guava 17.0+,是否有 better/more 惯用的方法来实现类似 DirectExecutorService 的行为?
Is there better/more idiomatic way to achieve DirectExecutorService like behavior if one can't upgrade to guava 17.0+?
如果这是你唯一的问题,你应该使用 MoreExecutors.sameThreadExecutor()
. It's basically newDirectExecutorService()
before it was moved to a new method (and directExecutor()
was added), see Javadoc:
Since: 18.0 (present as
MoreExecutors.sameThreadExecutor()
since 10.0)
顺便说一句:你真的应该升级到最新的番石榴,你用的是快六岁的番石榴了!