In the previous exercise, you learned that you can convert a class to run as a thread by extending the Thread
class. Java allows us to make classes threaded in another way as well, by implementing the Runnable
interface.
This approach is often the preferred one because you are only allowed to extend one class, and wasting it on Thread
might not be beneficial to your program. Here, rather than extending the capability of the built-in Thread
class, we just want to use its threading capability. Because of this, implementing the Runnable
interface, which is what the Thread
class does anyways, and passing the object into a new Thread
object is the preferred way of implementing them. Here’s how we would implement our Factorial
example by implementing Runnable
instead of extending Thread
.
public class Factorial implements Runnable { private int n; public Factorial(int n) { this.n = n; } public int compute(int n) { // ... the code to compute factorials } public void run() { System.out.print("Factorial of " + String.valueOf(this.n) + ":") System.out.println(this.compute(this.n)); } public static void main(String[] args) { Factorial f = new Factorial(25); Factorial g = new Factorial(10); Thread t1 = new Thread(f); Thread t2 = new Thread(f); t1.start(); t2.start(); } }
Another way of using the Runnable
interface, which is even more succinct, is to use lambda expressions. This is a more modern syntax that allows you to define the run
method you want to use inline, without requiring the class to implement Runnable
or extend Thread
. For the above example, this syntax looks like this:
public class Factorial { public int compute(int n) { // ... the code to compute factorials } public static void main(String[] args) { Factorial f = new Factorial(); // the lambda function replacing the run method new Thread(() -> { System.out.println(f.compute(25)); }).start(); // the lambda function replacing the run method new Thread(() -> { System.out.println(f.compute(10)); }).start(); } }
Behind the scenes, this syntax is still using the Runnable
interface. This is because Java will translate this lambda syntax into something like:
new Thread(new Runnable() { void run() { System.out.println(f.compute(25)); } }).start();
Using the lambda expression syntax for starting threads, you can create a threaded version of your class in only a few lines!
This syntax has several benefits:
- Your class no longer needs to extend
Thread
OR implementRunnable
. You can make a thread from anything! - Your class no longer needs a constructor to store arguments! Since you can pass an argument directly into the compute function when you create your thread, we no longer need to create a separate instance of our object any time we want to perform a threaded task with it.
- Your class is easier to read! The lambda syntax makes it so that people reading your code can immediately identify what task is being performed in your thread, without having to read your class first to find the
run
method.
Now, let’s try both of these methods of implementing Runnable
with our FortuneTeller
class.
Note: Sometimes you will see developers specifically import the Thread and Runnable classes from java.lang
. This is not necessary since all Java programs naturally import the java.lang
package anyways. It is often used to help with readability or remind an author to add a specific feature.
Instructions
Let’s start by converting our class into one that implements Runnable
instead of one that extends Thread
:
- Update the class to implement the
Runnable
interface, don’t forget to remove the Thread extension - Lastly, remember to remove the
@Override
annotation, as it is no longer required.
Great! Now that you’ve converted the CrystalBall
class to a Runnable
, update FortuneTeller.java so that instead of calling .start()
directly on an instance (c
) of CrystalBall
, it creates a new Thread t
from the now Runnable
version of the CrystalBall
, and call .start()
on that thread.
Run the code now, and you should see that this works in the exact same way as your previous code did when you extended the Thread
class!
Like before, you still require a new CrystalBall
for every Question
(q
) that is asked. In practice, a single Crystal Ball should be able to think about more than one question at a time!
Now let’s try implementing lambdas. You can use the lambda expression syntax for creating Runnable
s to achieve this as you saw in the Factorial
example.
Update CrystalBall.java class to:
- Neither extend
Thread
nor implementRunnable
- Remove the
run
method - Remove instance variable
question
- Remove the constructor
Finally, update FortuneTeller.java so that instead of creating a new CrystalBall c
in every iteration of the loop, it creates a CrystalBall c
before the loop over the questions
begins.
Then, in the loop, it should start a new Thread
using the lambda expression syntax, which calls the .ask
method of the CrystalBall
c
with the current Question
q
.
That should be it; run the code to see how the threaded FortuneTeller
program works! Feel free to tweak it further to see how you can make the output easier to understand.