Threads in Java
Implementing a New Thread
In Java, there are two ways to implement a new thread.
The first implementation option involves creating a class that implements the Runnable interface, which contains the void run() method. The code representing the logic of the new thread will be placed inside this method.
public class Task implements Runnable {
public void run() {
System.out.println("Hello from my new thread!");
}
}
Another way to implement a thread involves creating a class that extends the Thread class and overrides the void run() method within it. Similar to the first case, the logic of the new thread will be implemented within this method.
public class MyThread extends Thread {
public void run() {
System.out.println("Hello from my new thread!");
}
}
Running the New Thread in Parallel
In the case where the mechanism of implementing the Runnable interface has been used, to create a new thread that contains the logic defined in the Task class, an instance of the Task class will be created and provided as a parameter to the constructor of the Thread class. To run the new thread created using the constructor in parallel, you will call its public void start() method.
public class Main {
public static void main(String[] args) {
Thread t = new Thread(new Task());
t.start();
}
}
If the Thread class has been extended to implement a new type of thread, a new thread can be created by directly instantiating the MyThread class. To start the parallel execution of this thread, the public void start() method inherited from the Thread class should be called.
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}
There is a crucial distinction between the start() and run() methods of the Thread class! When the run() method is invoked, the code within it will execute sequentially within the thread that called it. When the start() method is invoked, the Java Virtual Machine (JVM) creates a new thread that will execute the instructions within the run() method concurrently with the thread that called the start() method.
Waiting for the Termination of a Thread's Execution
To wait for the termination of a thread's execution, Java provides us with the public final void join() method of the Thread class. It's important to note that this method can throw exceptions like InterruptedException.
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Sending Parameters to a Thread and Obtaining Results from It
To send parameters to a thread, the constructor of the class encapsulating the thread logic will be used, regardless of the implementation method (either through inheritance or interface implementation). To obtain a result from a thread after it has finished execution (when the join() method call has returned), we can use getter methods that return the result or directly access the result field if it is defined as public.
public class Task extends Thread {
private int id;
private int result;
public Task(int id) {
this.id = id;
}
public void run() {
result = id * id;
}
public int getResult() {
return result;
}
}
public class Main {
public static void main(String[] args) {
int NUMBER_OF_THREADS = 4;
Thread[] t = new Thread[NUMBER_OF_THREADS];
for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
t[i] = new Task(i);
t[i].start();
}
for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
try {
t[i].join();
System.out.println("Thread " + i + " computed result " + ((Task)t[i]).getResult() + ".");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}