Basic of Thread
In concurrent programming, there are two basic units of execution: processes and threads. In the Java, concurrent programming is mostly concerned with threads.
Threads are sometimes called lightweight processes. Both processes and threads provide an execution environment, but creating a new thread requires fewer resources than creating a new process. Threads share the process’s resources, including memory and open files.
For instance, in word processor application many tasks are executed at the same time – responding to user, spelling checking, formatting and certain backgrounds tasks etc. These tasks are executed as a thread share word processor resources.
So what exactly is a thread?
- An instance of class
java.lang.Thread
- A thread of execution
An instance of Thread
is just an object. Like any other object in Java, it has variables and methods, and lives and dies on the heap
But a thread of execution is an individual process (a lightweight process) that has its own call stack. (a call stack is a stack data structure that stores information about the active subroutines of a computer program).
Even if you don’t create any new threads in your program, threads are back there running.
The main()
method, that starts the whole thing, runs in one thread, called the main thread. But as soon as you create a new thread, a new stack materializes and methods called from that thread run in a call stack that’s separate from the main()
call stack. That second new call stack is said to run concurrently with the main thread.
How to create threads
A Java thread can be created in two ways
- Extending
java.lang.Thread
- Implementing
java.lang.Runnable
Extending the Thread class
You’ll first consider how to extend the Thread
class. You need to override the run()
method when you want to extend the Thread
class. If you don’t override the run()
method, the default run()
method from the Thread
class will be called, which does nothing.
The simplest way to define code to run in a separate thread is to
- Extend the
java.lang.Thread
class - Override the
run()
method.
To override the run()
method, you need to declare it as public
; it takes no arguments and has a void return type – in other words, it should be declared as public void run()
.
A thread can be created by invoking the start()
method on the object of the Thread
class. When the JVM schedules the thread, it will move the thread to a runnable state and then execute the run()
method. When the run()
method completes its execution and returns, the thread will terminate.
Example: MyThread.java
package com.javalatte.itcuties.basicofthread; /** * How to create thread by extending thread class */ public class MyThread extends Thread { /** * Always override run() method of Thread class, otherwise default run() * method will run which does nothing */ @Override public void run() { System.out.println("Welcome to itcuties.com"); System.out.println("Thread name in run() : " + getName()); } public static void main(String[] JavaLatte) { // thread creation Thread thread1 = new MyThread(); //starting thread thread1.start(); System.out.println("Thread name in main() : " + Thread.currentThread().getName()); } }
In this example, we’ll use getName()
instance method of thread class to get the name of the thread. Because main()
is a static
method, you don’t have access to this reference. So you get the current thread name using the static
method currentThread()
in the Thread
class.
Example: MyThreadWithoutRun.java
package com.javalatte.itcuties.basicofthread; /** * This class demonstrate what would happen when we don't override run() */ public class MyThreadWithoutRun extends Thread { public static void main(String[] JavaLatte) { Thread thread1 = new MyThreadWithoutRun(); thread1.start(); // no output because we haven't declare anything to run as a output } }
When we don’t override run()
method. No output will come as we haven’t declare any task to be executed as a thread.
An important note: you do not invoke the run()
method directly. Instead you start the thread using the start()
method; the run()
method is invoked automatically by the JVM.
The limitation with this approach is that if you extend Thread
, you can’t extend anything else.
Implementing the Runnable interface
The Thread
class itself implements the Runnable
interface. Instead of extending the Thread
class, you can implement the Runnable
interface. The Runnable
interface declares a single method, run()
.
//in java.lang package public interface Runnable { public void run(); }
Implementing the Runnable
interface gives you a way to extend any class you like, but still define behavior that will be run by a separate thread
When you implement the Runnable
interface, you need to define the run()
method. But Runnable
does not have start()
method. So, how do you create a thread if you implement the Runnable
interface? Thread
has an overloaded constructor, which takes a Runnable
object as an argument – Thread(Runnable target)
You can use this constructor to create thread from a class that implements Runnable
interface.
Example: MyRunnable.java
package com.javalatte.itcuties.basicofthread; /** * How to implement Runnable Interface to create thread */ public class MyRunnable implements Runnable { public void run() { System.out.println("Welcome to itcuties.com"); System.out.println("Thread name in run() : " + Thread.currentThread().getName()); } public static void main(String[] JavaLatte) { // thread creation Thread thread1 = new Thread(new MyRunnable()); //starting thread thread1.start(); System.out.println("Thread name in main() : " + Thread.currentThread().getName()); } }
Besides the no-arg constructor and the constructor that takes a Runnable
there are other overloaded constructors in class Thread
. The constructors we care about are:
Thread()
Thread(Runnable target)
Thread(Runnable target, String name)
Thread(String name)
Example: ThreadConstructor.java shows how to set the name of the threads.
package com.javalatte.itcuties.basicofthread; /** * How to use Thread(Runnable target, String name) constructor */ class Task implements Runnable { public void run() { System.out.println("Runnable Task is executed here"); System.out.println("Name of this Runnable thread : " + Thread.currentThread().getName()); } } public class ThreadConstructor { public static void main(String[] JavaLatte) { Thread thread1 = new Thread(new Task(), "RunnableTask"); thread1.start(); } }
Remember, every thread of execution begins as an instance of class Thread
. Regardless of whether your run()
method is in a Thread
subclass or a Runnable
implementation class, you still need a Thread
object to do the work.
How to start and run Multiple threads
In the above, we already have two thread, one is main()
method stats in a thread of its own and thread.start()
started a second thread.
In this example, we create a single Runnable
instance and 3 Thread
instances. All of these get the same Runnable
instance and each thread will given a unique name.
Example: MultipleThread.java
package com.javalatte.itcuties.basicofthread; /** * How to start and run multiple thread */ class PrintTask implements Runnable { public void run() { for (int i = 0; i < 5; i++) { System.out.println("Print by " + Thread.currentThread().getName() + " : " + i); } } } public class MultipleThread { public static void main(String[] JavaLatte) { Thread t1 = new Thread(new PrintTask()); t1.setName("Thread1"); Thread t2 = new Thread(new PrintTask()); t2.setName("Thread2"); Thread t3 = new Thread(new PrintTask()); t3.setName("Thread3"); t1.start(); t2.start(); t3.start(); } }
When you run this code multiple time, each time you’ll get different order of the thread printing value because that there is nothing in the Java specification that says threads will start running in the order in which they were started. And there is no guarantee that once a thread starts executing, it will keep executing until it’s done. Each thread will start, and each thread will run to completion.
Point to remember is Once a thread has been started, it can never be started again. If you try to call start()
a second time, it will cause an exception an IllegalThreadStateException
. Only a new thread can be started, and then only once. A runnable thread or a dead thread cannot be restarted.
Choose between extend the Thead and implement the Runnable
When you are sure that you class is not going to extend any class other than Thread
class, then you choose extending Thread
class. Even you can use getName()
method directly whereas you need to write Thread.currentThread().getName()
while implementing Runnable
interface.
There is limitation with the approach of extending Thread
class that you can’t extend anything. So, implementing Runnable
is a little convenient in this case.
Both the techniques are useful and mostly equivalent for problem solving.
Start() and Run() Methods
What we have seen is that we override the run()
method but invoke the start()
method. What will happen if you call run()
method instead of start()
method. Execute the following example and analyze the output.
Example : ThreadRunDemo.Java
package com.javalatte.itcuties.basicofthread; /** * This show what will happen if you call run() instead of start() method */ class Task2 implements Runnable { public void run() { System.out.println("Thread name in run() : " + Thread.currentThread().getName()); } } public class ThreadRunDemo { public static void main(String[] JavaLatte) { // thread creation Thread thread1 = new Thread(new Task2()); //starting thread thread1.run(); System.out.println("Thread name in main() : " + Thread.currentThread().getName()); } }
Sample Output:
Thread name in run() : main Thread name in main() : main
If you call the run()
method directly, it simply executes as part of the calling thread. It does not execute as a thread: it doesn’t get scheduled and get called by the JVM. That is why the getName()
method in the run()
method returns main instead of Thread-0.
Download this sample code here.
Very nice