Java Multithreading - Concurrency in Java

What is multithreaded programming?

Multithreaded programming is a system where one program can have multiple segments which can run simultaneously. Each of the segments is independent of each other and is called Thread.

What is Process based multitasking? How it is different from Thread based multitasking?

Almost all of us have used process based multitasking system. For example, all the operating systems we are using are process based multitasking system. Because, they allows us to work on multiple processes at the same time. For example, while writing this article, I am using Microsoft office and Microsoft Paint application at the same time. So, my operating system is allowing me to run at least 2 processes (MS Paint and MS Word) at the same time. This is Process based multitasking system.

So, how it is different from thread based systems?

Threads are light weight processes. But, threads run within a same program. So, we can say that, thread is lightweight process inside a single program. And, in a multithreaded system, a single program can run more than one threads at a time.

By default, java supports multithreading programming. Till now we haven’t consciously created threads. But, yes we did. All of us remember, when writing a simple java program, we use:

public static void main (String args[]) 

When we used this, the main thread is automatically created.

Thread Lifecycle

A thread has a specific life cycle. There are several stages in it. Let us discuss about them:

  1. New: In this state, the thread has been generated, but the start method() has not been called.
  2. Runnable: In this stage, the start() method has already called, but it is not running yet.
  3. Running: The thread is running (the thread scheduler has selected this thread for run).
  4. Blocked:  The thread is alive, but not eligible to run now.
  5. Terminated: The thread is terminated.

Graphical representation for the thread lifecycle

 

Thread priorities

In Java, every thread is given a priority. This priority is used by the scheduler to decide which thread to run first. The priority lies between the ranges – MIN_PRIORITY (which is equals to 1) and MAX_PRIORITY (which is equals to 10). Default priority for every thread (when the priority is not assigned manually) is equals to NORM_PRIOROTY (which is equals to 5).

Note:

  1. MAX_PRIORITY, MIN_PRIORITY, NORM_PRIOROTY – all these variables are static final variables within the thread.
  2. Threads with lower priority can be preempted by the threads with higher priority.

Creating a thread in Java

There are 2 basic ways of creating threads in Java. In the below section, we discuss about both the processes.

1. Creating threads by implementing Runnable Interface

package src.thread;

class MyThread implements Runnable 
{
	public static void main(String args[]) 
	{
		MyThread myThread = new MyThread();
		Thread t = new Thread(myThread);
		t.start();
	}
	public void run() 
	{
		System.out.println("MyThread is Running.");
	}
}

MyThread myThread = new MyThread();

In this line, we are creating an object of the class in which we want to create the thread.

Thread t = new Thread(myThread);

Here we are creating thread class object explicitly in this line using the object of class MyThread, Otherwise it will not be considered as a thread object.

The output will be:       

MyThread is Running.

2. Creating threads by extending Thread Class

package src.thread;

class MyThread1 extends Thread 
{

	public static void main(String args[]) 
	{
		MyThread1 t = new MyThread1();
          	t.start();
   	}

   	public void run() 
	{
          	System.out.println("MyThread1 is Running");
   	}
}

In this method of creating a thread by extending Thread class, when an object is created of MyThread1 class, is actually creates a thread.

The output will be:

MyThread1 is Running

Assigning priority to the threads

Java allows to set name explicitly to the threads (setName()). Similarly, Java also has method to retrieve the name for a thread(getName()).

Example (Setting name to a thread): 

Thread t = new Thread(myThread);
t.setName("Thread_Name");

Example (Getting name of a thread):

System.out.println(t.getName());

Note: Default name for a thread is Thread-0. Try running the following code for explanation.

package src.thread;

class MyThread implements Runnable 
{
       	public static void main(String args[]) 
	{
              MyThread myThread = new MyThread();
              Thread t = new Thread(myThread);
              System.out.println(t.getName());
              t.start();
       	}
       	public void run() 
	{
       	}
}

Output is:

Thread-0

Assigning priority to the threads

As we already know, priorities are used to provide importance of the threads in front of the CPU schedulers. Java has method setPriority() and getPriority for setting the priority and retrieving the priority respectively.

Example:

package src.thread;

class MyThread implements Runnable 
{
       	public static void main(String args[]) 
	{
              MyThread myThread = new MyThread();
              Thread t = new Thread(myThread);
              t.setPriority(10);
              t.start();
              System.out.println(t.getPriority());
       	}
       	public void run() 
	{
       	}
}

Output is:

10

What will happen if we call run() without calling start() method?

Before discussing this, let us see, what will happen when we run 2 separate threads concurrently with same priority.

package src.thread;

class MyThread implements Runnable 
{
       	public static void main(String args[]) 
	{
              	MyThread myThread = new MyThread();
              	Thread t1 = new Thread(myThread);
              	t1.setPriority(5);
              	Thread t2 = new Thread(myThread);
              	t2.setPriority(5);
              	t1.start();
              	t2.start();
       	}
       	public void run() 
	{
              	for(int i = 0; i<80; i++) 
		{
                     	System.out.print(i + " ");
              	}
              	System.out.println();
       	}
}

Calling Method of a Java Class from Thread

Threads can call a method of a java class which implements runnable. This method can perform some operation when thread will call it. In below code we are creating a class Adder in which doAdd() performs the sum of two numbers passed from the parent thread.

public class Adder implements Runnable
{
	private int a;
	private int b;
	public Adder(int i, int j)
	{
		a = i;
		b = j;
	}
	public void doAdd()
	{
		System.out.println(a+b);
	}
	public void run()
	{
		try
		{
			doAdd();
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
		}
	}
}

public static void main(String[] args) {
		
	int[] n1 = {1,2,3,4,5};
	int[] n2 = {1,2,3,4,5};
	Thread[] threads = new Thread[5];
		
	for(int i =0; i< n1.length; i++)
	{
		Adder adder = new Adder(n1[i], n2[i]);
		threads[i] = new Thread(adder);
		threads[i].start();
	}
}

Output of Above Program will be:

2
6
4
10
8

Join Threads

There is no parent/child relationship between threads in Java. If thread A creates thread B and then thread A finishes/ terminates, then thread B will continue to execute. The exception to this is will be when the main thread that runs the main() function terminates then the process terminates and all other threads stop.

To avoid such situation we use Join. When using join, the main thread will wait for all the threads to finish which got created by main thread.

Below program shows the use of Join.

public class Adder implements Runnable
{
	private int a;
	private int b;
	public Adder(int i, int j)
	{
		a = i;
		b = j;
	}
	public void doAdd()
	{
		System.out.println(a+b);
	}
	public void run()
	{
		try
		{
			doAdd();
		}
		catch(Exception e)
		{
			System.out.println(e.getMessage());
		}
	}
}


public static void main(String[] args) {
		
	int[] n1 = {1,2,3,4,5};
	int[] n2 = {1,2,3,4,5};
	Thread[] threads = new Thread[5];
		
	for(int i =0; i< n1.length; i++)
	{
		Adder adder = new Adder(n1[i], n2[i]);
		threads[i] = new Thread(adder);
		threads[i].start();
	}
		
	for(Thread t:threads)
	{
		try {
			t.join();
		}
		catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

 


< Prev
Scroll to Top