我可以将Guava的AbstractExecutionThreadService用于需要中断的服务吗?

时间:2012-08-15 00:51:37

标签: java service guava interrupt

我有一项服务,我想将其作为Google Guava Service实施。

该服务基本上运行while (true)循环,在事件到达BlockingQueue时对其进行处理。简化的示例代码可在此处获取:

https://gist.github.com/3354249

问题是代码阻塞BlockingQueue#take(),因此停止服务的唯一方法是中断其线程。这可能是使用Guava的AbstractExecutionThreadService吗?

当然,在这种情况下,我可以使用queue.take()用轮询循环替换queue.poll(1, TimeUnit.SECONDS),从而消除了线程中断的需要。但是:

  • 出于性能和代码可读性的原因,我想避免这样做

  • 在其他情况下,无法避免线程中断,例如如果在从InputStream读取字节时阻止了服务。

2 个答案:

答案 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()方法中断它。