6

Inspired by this SO question, I'd like to better understand the contract defined by Runnable, along with when it is and is not acceptable to use the Runnable interface. We know that Runnable's most common use-case is to be implemented by a class that is intended to be executed on a separate thread:

Runnable task = () -> {
    // Code to be executed on another thread
};
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(task);

This yields two questions:

  1. Is this the only acceptable pattern for its usage?
  2. If not, what are other acceptable usage patterns for Runnable that do not revolve around multithreading?

The use-case in the original SO question is to create a generic way to execute code before and after a method's execution without repetition, such as:

public void example1() {
    before();
    method1();
    after();
}

public void example2() {
    before();
    method2();
    after();
}

Eran's answer proposes using Runnable to allow encapsulation of the logic for each method, along with a single method that accepts the Runnable as an argument and invokes Runnable#run at the appropriate time:

public void call(Runnable method){
    before();
    method.run();
    after();
}

call( () -> { method1(); });
call( () -> { method2(); });

However, using Runnable in this fashion was met with some disagreement, stating that this is not an acceptable use of Runnable. Is this is a valid concern, or a matter of stylistic opinion?

From my own research, the Javadoc for Runnable states the following contract, which seems to still be upheld with this usage of Runnable:

The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread.

Further, if usage outside of the context of multithreading were discouraged or disallowed, I would expect either:

  1. The Javadoc to mention it somewhere, or
  2. Runnable to be in a concurrency-specific package, e.g. java.util.concurrent, however it is available in java.util.
  • The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. -- That seems pretty clear to me. – Robert Harvey Dec 15 '15 at 22:32
  • 1
    @RobertHarvey not to me. Yes, it states that it should be used for objects intended to be run by a thread, but (1) it doesn't state that it can't also be used for other purposes and (2) as all code that is executed is executed (potentially indirectly) by a thread, isn't the statement somewhat meaningless anyway? – Jules Dec 16 '15 at 16:37
  • 1
    @Jules See nickB's answer, below. – Robert Harvey Dec 16 '15 at 16:45
7

While it is still clearly a matter for debate among the community, Alex revealed a comment left by Brian Goetz, Java Language Architect at Oracle, which I believe is the definitive canonical answer to this question. I've copied his comments below, emphasis mine:

The EG discussed this in some detail and concluded that it was better to coopt Runnable than to create the opportunity for confusion because there were two competing abstractions. In a perfect world, we might have rewritten the Javadoc to reflect that its use had (long ago) become broader than was imagined in 1995, but the reality is that well before 2014 it became broadly understood that Runnable was the JDK's primary "task" abstraction, regardless of its connection with threads.

4

Streams and lambda expressions invite the use of single-method interfaces. It's easy enough to write a new interface, but also tempting to simply re-purpose one that the system provides instead of littering the code base with tiny interfaces.

However, when an interface is strongly associated with something, remapping its semantics sets the stage for a future maintainer of the code to waste time looking for things that aren't there. In the case of Runnable, the future maintainer might waste time by hunting for the elusive concurrency they think is subtly embedded in the code.

Although you could add comments everywhere, it would be neater to just create your own interface, establishing your own semantics explicitly.

0

I would argue that using Runnable outside the context of multi threading would be misleading as to the intent of your code. Sure you can use it outside that context because after all it's just an interface with a no arg run method. But it might be better to create your own interface that is more specific as to the intent of what the code does.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.