我有一项服务,我想将其作为Google Guava Service
实施。
该服务基本上运行while (true)
循环,在事件到达BlockingQueue
时对其进行处理。简化的示例代码可在此处获取:
https://gist.github.com/3354249
问题是代码阻塞BlockingQueue#take()
,因此停止服务的唯一方法是中断其线程。这可能是使用Guava的AbstractExecutionThreadService
吗?
当然,在这种情况下,我可以使用queue.take()
用轮询循环替换queue.poll(1, TimeUnit.SECONDS)
,从而消除了线程中断的需要。但是:
出于性能和代码可读性的原因,我想避免这样做
在其他情况下,无法避免线程中断,例如如果在从InputStream
读取字节时阻止了服务。
答案 0 :(得分:6)
您可以覆盖executor()
方法以提供您自己的执行程序,然后执行程序将对线程的引用存储到您的字段中。然后,如果需要,您可以轻松地中断线程。
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import com.google.common.util.concurrent.AbstractExecutionThreadService;
public abstract class InterruptibleExecutionThreadService extends AbstractExecutionThreadService {
private final AtomicReference<Thread> runningThread = new AtomicReference<Thread>(null);
@Override
protected Executor executor() {
return new Executor() {
@Override
public void execute(Runnable command) {
Thread thread = Executors.defaultThreadFactory().newThread(command);
runningThread.compareAndSet(null, thread);
try {
thread.setName(serviceName());
} catch (SecurityException e) {
// OK if we can't set the name in this environment.
}
thread.start();
}
};
}
protected void interruptRunningThread() {
Thread thread = runningThread.get();
if (thread != null) {
thread.interrupt();
}
}
}
答案 1 :(得分:3)
如果你想使用AbstractExecutionThreadService
,我不认为打断线程是一个选项,因为没有任何方法可以获得对线程的引用来调用interrupt()
。< / p>
如果您正在使用BlockingQueue,则必须在while循环内轮询以检查服务是否仍在运行,或者您可以使用sentinel值来警告其需要停止的worker方法。
示例:
轮询:
while(isRunning()) {
Value v = queue.poll(1, TimeUnit.SECONDS);
// do something with v
}
Sentinal值:
while(isRunning()) {
Value v = queue.take();
if(v == POISON) {
break;
}
// do something with v
}
我个人会尝试使用轮询解决方案,看看性能如何。您可能会对实际影响性能的影响感到惊讶。
至于从InputStream读取,如果InputStream是长寿的并且有可能无限期地阻塞,我认为使用AbstractExecutionThreadService
是不可能的。您应该使用AbstractService
来创建并保存对其自己的执行线程的引用,以便您可以使用doStop()
方法中断它。