In Java, there're some ways to run your code in a multi-threaded environment such as inheriting the Thread
class, implementing the Runnable
interface, and implementing the Callable
interface
Let's walk through this tutorial to see the examples in details
Inherit the Thread
class
You can inherit your class from the Thread
class and override the run()
method with the code you want to be executed by multiple threads
Let's define an example class as below
The ThreadCounter
class extends the Thread
class and override the run()
method to implement a counter from 1 to 100
In ThreadTest
, we created an instance of ThreadCounter
and execute it in an ExecutorService
with a fixed thread pool
Inheriting the Thread
class is not a recommended way if you are only planning to override the run()
method and not enhancing or modifying the default behaviour of the Thread
class. Composition is preferred over inheritance in most cases. So let's try the composition way
Implement the Runnable
interface
The Runnable
interface is designed for running in multi-threaded environment. The Thread
class actually is a Runnable
implementation
Let's define a class that implementing the Runnable
interface as the following
In the RunnableCounter
class, we overrode the run()
method of the Runnable
interface to provide the code we want to run in multi-threading environment
We also created an instance of RunnableCounter
and execute it in an ExecutorService
with a fixed thread pool
In most cases, as pointed out in the previous section, implementing the Runnable
interface is preferred to inheriting the Thread
class
Runnable
, so Thread
, can not return the result and cannot throw a checked exception, that's why we have to pass the AtomicInteger
instance into ThreadCounter
and RunnableCounter
constructors
Object result = executorService.submit(new RunnableCounter(counter)).get();
assertThat(result).isNull();
assertThat(counter.get()).isEqualTo(100);
There's a better and more official way to get the result
Implement the Callable interface
The Callable
interface is similar to the Runnable
but return a result and may throw an exception
Let's define a class that implementing the Callable
interface as the following
In the CallableCounter
class, we overrode the call()
method of the Callable
interface to provide the code we want to run in multi-threading environment
Unlike the run()
method of Runnable
, call()
can throw an Exception
In CallableTest
, we wrote a unit test case to execute the CallableCounter
in multiple threads. We can get the result returned from the Callable
with the get()
method of the Future
class instance returned by the ExecutorService
AtomicInteger result = executorService.submit(new CallableCounter()).get();
assertThat(result.get()).isEqualTo(100);
Conclusion
In this tutorial, we learned how to creating a new thread of execution, the difference between Thread
, Runnable
and Callable
and when to use them. You can find the full source code here