Multithreading in Java: The Complete Guide

Usually, in a standard Java program, the entire program runs as a single “thread.” What it means that if a part of the program is demanding some I/O resource, for example, to proceed further, and if that I/O resource is not available at the moment, the entire program will be waiting and won’t also execute until and unless that particular resource in demand is freed. Multithreading in Java is a process of implementing multiple threads simultaneously.

Multitasking is when multiple processes share common processing resources, such as a CPU. Multithreading extends the idea of multitasking into applications where you can subdivide the specific operations within a single application into individual threads.

Each of the threads can run in parallel within the system. This is because the OS divides the processing time among applications and each thread within the application.

Multithreading enables you to write so multiple activities can proceed concurrently in the same program.

As one can understand, this raises an issue in how fast the program gets executed. The address this issue, the concept of multithreading was developed wherein the program has multiple threads that can run in parallel.

What is a thread in Java?

A thread in Java is the lightweight sub-process, the smallest processing unit. Multiprocessing and multithreading are both used to achieve multitasking. There can be multiple processes inside an OS, and one process can have multiple threads.

Multitasking

Multitasking is the process of executing multiple tasks simultaneously. We use multitasking to utilize the CPU. Multitasking can be achieved in two ways:

  1. Process-based Multitasking (Multiprocessing)
  2. Thread-based Multitasking (Multithreading)

1) Process-based Multitasking (Multiprocessing)

  1. Each process has an address in memory. In other words, each process allocates a separate memory area.
  2. A process is heavyweight.
  3. The cost of communication between the process is high.
  4. Switching from one process to another requires some time for saving and loading registers, memory maps, updating lists, etc.

2) Thread-based Multitasking (Multithreading)

  1. Threads share the same address space.
  2. A thread is lightweight.
  3. The cost of communication between the thread is low.

Java Multithreading

Java is the multi-threaded programming language, meaning we can develop a multi-threaded program using Java.

A multi-threaded program contains two or more parts that can run concurrently. Each part can handle the different tasks simultaneously, making optimal use of the available resources, especially when your computer has multiple CPUs.

Life Cycle of a Thread

Life Cycle of a Thread

As shown in the above figure, a thread is executed inside a process. There is context-switching between the threads. There can be multiple processes inside the OS, and one process can have multiple threads.

Following are the stages of the life cycle −

  • New − A new thread begins its life cycle in the new state. It remains in this state until a program starts the thread. It is also referred to as the born thread.
  • Runnable − After a newly born thread is started, the thread becomes runnable. The thread in this state is considered to be executing its task.
  • Waiting − Sometimes, a thread transitions to the waiting state while a thread waits for another to perform the task. Thread transitions back to the runnable state only when another thread signals the waiting thread to continue executing.
  • Timed Waiting − A runnable thread can enter the timed waiting state for the specified time interval. A thread in this state transitions back to the runnable state when that time interval expires or when the event it is waiting for occurs.
  • Terminated (Dead) − A runnable thread enters the closed state when it completes its task or terminates.

The Main Thread

When a Java program starts, one thread starts running – the main thread.

The main thread is essential because it is the thread from which other child threads will be created, and the main thread is often the last to finish execution as it generally performs various shutdown functions.

The main thread is created automatically when the program starts, and it can be controlled using a Thread object, as shown in the example below.

// MainThreadDemo.java

class MainThreadDemo{
	public static void main(String [] args){
		Thread t = Thread.currentThread();
		System.out.println("Main thread: " + t); 
		
                //now changing the name of the thread
		t.setName("New Name");
		System.out.println("Name changed to: " + t);
	}
}

OUTPUT

Java Multithreading Tutorial With Example

Here, the first println statement prints the name of the thread as “main,” which has priority “5”, and belongs to a group of threads called “main.”

Using the setName() method, the thread’s name was changed to “New Name.”

Thread Priority

Every thread has a Thread Priority. If a thread is currently being executed in the CPU, and a thread of higher priority then demands the CPU, the thread with the lower priority will be preempted, and a context switch will happen, giving the CPU to the higher-priority thread.

There is another way in which a thread can give up control. It is by voluntarily yielding, sleeping, or blocking on pending I/O.

Creating a Thread

In Java, there are two ways to create a thread.

  1. By implementing the Runnable interface
  2. By extending the Thread class

Implementing Runnable Thread

See the following code of the SecondThread.java file.

// SecondThread.java

class SecondThread implements Runnable{
	Thread t;

	SecondThread(){
		t=new Thread(this, "Second Thread");
		System.out.println("Second Thread: " + t);
		t.start();

	}

	public void run(){
		try{
			for(int i=5; i>0; i--){
				System.out.println("Second Thread: " + i);
				Thread.sleep(1000);
			}
		}catch(InterruptedException e){
			System.out.println("Second interrupted.");
		}
		System.out.println("Exiting Second Thread");
	}
}

class RunnableDemo{
	public static void main(String [] args){
		new SecondThread();

		try{
			for(int i=5; i>0; i--){
				System.out.println("Main Thread: " + i);
				Thread.sleep(2000);
			}
		}catch(InterruptedException e){
			System.out.println("Main interrupted.");
		}
			
		
		System.out.println("Main thread exiting.");
	}
}

Output

Implementing Runnable Thread

The new thread is created by instantiating the SecondThread class. Then, in the constructor of the SecondThread class, an instance of the Thread class is created concerning the instance in which it is created (hence, the use of “this”).

The start() method calls the run() method, which holds the actual content of the new thread. The second thread finishes execution when the run() method returns.

The Thread.sleep(double milliseconds) method voluntarily lets the thread sleep for the time entered in milliseconds as a parameter.

This method throws the InterruptedException, which is not handled by it; hence, a try-catch mechanism must be used every time this method is called.

As seen in the output, the second and main threads execute simultaneously, with the second thread finishing first as it has been given less sleep time. The order of execution may vary from time to time.

Extending Thread

See the following example of the ExtendingThread.java file.

// ExtendingThreadDemo.java

class SecondThread implements Runnable{
	Thread t;

	SecondThread(){
		t=new Thread(this, "Second Thread");
		System.out.println("Second Thread: " + t);
		t.start();

	}

	public void run(){
		try{
			for(int i=5; i>0; i--){
				System.out.println("Second Thread: " + i);
				Thread.sleep(1000);
			}
		}catch(InterruptedException e){
			System.out.println("Second interrupted.");
		}
		System.out.println("Exiting Second Thread");
	}
}

class RunnableDemo{
	public static void main(String [] args){
		new SecondThread();

		try{
			for(int i=5; i>0; i--){
				System.out.println("Main Thread: " + i);
				Thread.sleep(2000);
			}
		}catch(InterruptedException e){
			System.out.println("Main interrupted.");
		}
			
		
		System.out.println("Main thread exiting.");
	}
}

Output:

Extending Thread in Java

Here, the SecondThread class extends the Thread class, which is instantiated to create a new thread. In the constructor of SecondThread, super() is used to pass on the name of the second thread to the constructor of Thread.

The rest of the program executes like the example of implementing Runnable.

Advantages of Java Multithreading

1) It doesn’t block a user because threads are independent, and you can perform multiple operations simultaneously.

2) You can perform many operations together, so it saves time.

3) Threads are independent, so it doesn’t affect other threads if an exception occurs in a single thread.

Thus, Java multithreading solves the problem of stuck programs due to resource allocation issues. Furthermore, since it handles a large part of the multithreading, the programming becomes easier to manage.

Thread Methods

Sr.No. Method & Description
1 public void start()

It starts a thread in the separate execution path, then invokes a run() method on this Thread object.

2 public void run()

If this Thread object was instantiated using the separate Runnable target, the run() method is invoked on that Runnable object.

3 public final void setName(String name)

Changes the name of a Thread object. There is also a getName() method for retrieving a name.

4 public final void setPriority(int priority)

Sets a priority of this Thread object. The possible values are between 1 and 10.

5 public final void setDaemon(boolean on)

A parameter of true denotes this Thread as the daemon thread.

6 public final void join(long millisec)

The current thread invokes this method on the second thread, causing a current thread to block until a second thread terminates or the specified number of milliseconds passes.

7 public void interrupt()

It interrupts this thread, causing it to continue if it was blocked for any reason.

8 public final boolean isAlive()

It returns true if the thread is alive, any time after it has been started but before it runs to completion.

The previous methods are invoked on a particular Thread object. The following methods in the Thread class are static. Invoking one of the static methods operates on the currently running thread.

Sr.No. Method & Description
1 public static void yield()

It causes a currently running thread to yield to any other threads of the same priority waiting to be scheduled.

2 public static void sleep(long millisec)

It causes the currently running thread to block for at least the specified number of milliseconds.

3 public static boolean holdsLock(Object x)

Returns true if a current thread holds the lock on a given Object.

4 public static Thread currentThread()

Returns a reference to a currently running thread, which is a thread that invokes this method.

5 public static void dumpStack()

Prints the stack trace for the currently running thread, which is useful when debugging a multi-threaded application.

That’s it for this tutorial.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.