Chris B
Chris B

Reputation: 9259

Can I use Guava's AbstractExecutionThreadService for services that need to be interrupted?

I have a service that I would like to implement as a Google Guava Service.

The service basically runs a while (true) loop that processes events as they arrive on a BlockingQueue. Simplified sample code is available here:

https://gist.github.com/3354249

The problem is that the code blocks on BlockingQueue#take(), so the only way to stop the service is to interrupt its thread. Is this possible using Guava's AbstractExecutionThreadService?

Of course, in this case I could replace queue.take() with a polling loop using queue.poll(1, TimeUnit.SECONDS), thus removing the need for thread interruption. However:

Upvotes: 6

Views: 3228

Answers (2)

Peter Štibraný
Peter Štibraný

Reputation: 32893

You can override executor() method to supply your own executor, which will then store reference to the thread into your field. Then you can easily interrupt the thread, if needed.

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();
        }
    }
}

Upvotes: 7

Mike Deck
Mike Deck

Reputation: 18397

I don't think interrupting the thread is really an option if you want to use an AbstractExecutionThreadService since there's not really any way to get a reference to the thread in order to call interrupt().

If you're using a BlockingQueue you either have to poll inside a while loop that checks if the service is still running, or you can use a sentinel value to alert the worker method that it needs to stop.

Examples:

Polling:

while(isRunning()) {
    Value v = queue.poll(1, TimeUnit.SECONDS);
    // do something with v
}

Sentinal value:

while(isRunning()) {
    Value v = queue.take();
    if(v == POISON) {
        break;
    }
    // do something with v
}

I personally would try the polling solution and see what the performance is like. You might be surprised by how little that really effects the performance.

As for reading from an InputStream, if the InputStream is long-lived and has the potential to block indefinitely I don't think using an AbstractExecutionThreadService is really possible. You should instead use an AbstractService which creates and holds a reference to its own execution thread so that you can interrupt it in the doStop() method.

Upvotes: 4

Related Questions