top of page
Writer's pictureApeksha Yadav

Introduction to Java multithreading

When we execute java application we need good processors for quicker execution of the application. However good processor alone can’t help to run application fast. To create performance efficient application, multithreading is required.


What is multithreading?

Multithreading is a concept in which the application can create a small unit of tasks to execute in parallel. If you are working on a computer, it runs multiple applications and allocates processing power to them. A simple program runs in sequence and the code statements execute one by one. This is a single-threaded application. But, if the programming language supports creating multiple threads and passes them to the operating system to run in parallel, it is called multithreading.


When we talk about multithreading, we don’t care if the machine has a 2-core processor or a 16-core processor. Our goal is to create a multithreaded application and let the OS handle the allocation and execution part. In short, multithreading has nothing to do with multiprocessing.


Java is multithread programming language which means we can develop multithreaded program using java. A multithreaded program contains two or more parts that can run simultaneously, and each part can handle different tasks at the same time making efficient use of available resources.


By definition, multitasking is when multiple processes share common processing resources such as CPU. Multithreading extends the idea of multitasking into applications where one can subdivide specific operations within single application into individual threads. Each thread can run in parallel. The OS divides processing time not only among different applications, but also among each thread within an application.


Thread creation in Java

Java supports multithreading through Thread class and implementing runnable interface.

Java thread allows user to create a lightweight process that executes some tasks. We can create multiple threads in our program and start them. Java runtime will take care of creating machine-level instructions and work with OS to execute them in parallel.


Using Thread class

public class Main extends Thread {
    public void run(){
        System.out.println(“Thread by extending thread class”);
    }
}

Using Runnable class

public class Main implements Runnable {
    public void run(){
		System.out.println(“Thread by implementing runnable interface”);
    }
}

Thread creation by extending thread class or implementing runnable class depends on purpose of your class.

If your class want to extend other class, then it is recommended to implement runnable interface.

The reason is, when thread is created by extending Thread class, user cannot extend any other class in Java.


Thread Life cycle

Thread goes through various stages in its life cycle. For example, thread is created, started, runs and then dies. Following diagram shows complete life cycle of a thread



New state:

A new thread begins its life cycle in the new state. It remains in this state until the program starts the thread


Runnable state:

After a newly created thread is started, the thread becomes runnable. A Java thread in the RUNNABLE state could either be actually running on a CPU, or it could be waiting for a CPU to run on, but it is not waiting for anything else.


Waiting state:

A thread waits for another thread to perform a task.A thread transitions back to the runnable state only when another thread signals the waiting thread to continue executing.


Timed waiting state:

A runnable thread can enter the timed waiting state for a specified interval of time. 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) state:

A runnable thread enters the terminated state when it completes its task or otherwise terminates.


Blocked state:

A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or reenter a synchronized block/method after calling Object.wait


Main thread in Java

When a Java program starts up, one thread begins running immediately. This is usually called the main thread of our program because it is the one that is executed when our program begins


There are certain properties associated with the main thread which are as follows:



  • Main thread is the thread from which other “child” threads will be spawned.

  • It must be the last thread to finish execution because it performs various shutdown actions


To control main thread, thread reference can be obtained using method currentThread(). This method returns a reference to main thread. Default priority of main thread is 5 and for all remaining user thread priority will be inherited from parent to child threads.


For each program, a Main thread is created by JVM(Java Virtual Machine). The “Main” thread first verifies the existence of the main() method, and then it initializes the class.


Thread priorities

Every Java thread has a priority that helps the operating system determine the order in which threads are scheduled.


Java thread priorities are in the range between MIN_PRIORITY (1) and MAX_PRIORITY (10). By default, every thread is given priority NORM_PRIORITY (5).


Threads with higher priority are more important to a program and should be allocated processor time before lower-priority threads. However, thread priorities cannot guarantee the order in which threads execute and very much platform dependent


Thread synchronization


Synchronization in Java is the ability to control the access of multiple threads to any shared resource. Java thread synchronization is better option where we want to allow only one thread to access the shared resource.


Synchronization is mainly used to prevent thread interference and to prevent consistency problems. There are two types of synchronization Process synchronization and Thread synchronization. We will see Thread synchronization in more details


There are two types of thread synchronization mutual exclusive and inter-thread communication.

 

  1. Mutual Exclusive

  2. Cooperation (Inter-thread communication in java)

Mutual Exclusive

Mutual Exclusive thread synchronization type helps keep threads from interfering with one another while sharing data. It can be achieved by using the following three ways:

  • By Using Synchronized Method

  • By Using Synchronized Block

  • By Using Static Synchronization


Synchronization is achieved using internal entity known as lock or monitor. Every object has a lock associated with it. A thread that needs constant access to an object's data or resource has to acquire the object's lock before accessing them, and then release the lock when it's done with them


Inconsistent behavior - Without thread synchronization

Here in this example, we are printing table of 5 and 100 using two threads. In the output, you can see table of 5 and 100 are printed in random order due to no synchronization between threads


Example:

class Table{  
void printTable(int n){ //method not synchronized  
   for(int i=1;i<=5;i++){  
     System.out.println(n*i);  
     try{  
      Thread.sleep(400);  
     }catch(Exception e){System.out.println(e);}  
   }  
 }  
}  
  
class MyThread1 extends Thread{  
    Table t;  
    MyThread1(Table t){  
        this.t=t;  
    }  
    public void run(){  
        t.printTable(5);  
    }  
 }  

class MyThread2 extends Thread{  
    Table t;  
    MyThread2(Table t){  
        this.t=t;  
    }  
    public void run(){  
        t.printTable(100);  
    }  
}  
  
class TestSynchronization1{  
public static void main(String args[]){  
  Table obj = new Table();//only one object  
  MyThread1 t1=new MyThread1(obj);  
  MyThread2 t2=new MyThread2(obj);  
  t1.start();  
  t2.start();  
  }  
}

Output

5
100
10
200
15
300
20
400
25
500

Consistent behavior - Using synchronization method option

Synchronized method is used to lock an object for any shared resource

When a thread invokes a synchronized method, it automatically acquires the lock for that object and releases it when the thread completes its task


Example:

// Example of java synchronized method  
class Table{  
 synchronized void printTable(int n){ //synchronized method  
   for(int i=1;i<=5;i++){  
     System.out.println(n*i);  
     try{  
      Thread.sleep(400);  
     }catch(Exception e){System.out.println(e);}  
   }  
  
 }  
}  
  
class MyThread1 extends Thread{  
  Table t;  
  MyThread1(Table t){  
    this.t=t;  
  }  
  public void run(){  
    t.printTable(5);  
  }   
}  

class MyThread2 extends Thread{  
  Table t;  
  MyThread2(Table t){  
    this.t=t;  
  }  
  public void run(){  
    t.printTable(100);  
  }  
}  
  
public class TestSynchronization2{  
  public static void main(String args[]){  
    Table obj = new Table();//only one object  
    MyThread1 t1=new MyThread1(obj);  
    MyThread2 t2=new MyThread2(obj);  
    t1.start();  
    t2.start();  
  }  
}

Output

5
10
15
20
25
100
200
300
400
500

Consistent behavior - Using synchronization block option

Synchronized block can be used to perform synchronization on any specific resource of the method.

Example:

class Table{  
 void printTable(int n){
   synchronized(this) {  //synchronized block
   for(int i=1;i<=5;i++){  
     System.out.println(n*i);  
     try{  
      Thread.sleep(400);  
     }catch(Exception e){System.out.println(e);}  
   }  
  }
 }  
}  
  
class MyThread1 extends Thread{  
  Table t;  
  MyThread1(Table t){  
    this.t=t;  
  }  
  public void run(){  
    t.printTable(5);  
  }  
}  

class MyThread2 extends Thread{  
  Table t;  
  MyThread2(Table t){  
    this.t=t;  
  }  
  public void run(){  
    t.printTable(100);  
  }  
}  

public class TestSynchronization2{  
  public static void main(String args[]){  
    Table obj = new Table();//only one object  
    MyThread1 t1=new MyThread1(obj);  
    MyThread2 t2=new MyThread2(obj);  
    t1.start();  
    t2.start();  
  }  
} 

Output :

5
10
15
20
25
100
200
300
400
500

Consistent behavior - Using static synchronization option

If you make any static method as synchronized, the lock will be on the class not on object.


Example:

class Table  
{     
 synchronized static void printTable(int n){    //static synchronization
   for(int i=1;i<=5;i++){    
     System.out.println(n*i);    
     try{    
       Thread.sleep(400);    
     }catch(Exception e){}    
   }    
 }    
}
    
class MyThread1 extends Thread{    
  public void run(){    
    Table.printTable(1);    
  }    
}    

class MyThread2 extends Thread{    
  public void run(){    
    Table.printTable(10);    
  }    
}    

class MyThread3 extends Thread{    
  public void run(){    
    Table.printTable(100);    
  }    
}    

public class TestSynchronization4{    
  public static void main(String t[]){    
    MyThread1 t1=new MyThread1();    
    MyThread2 t2=new MyThread2();    
    MyThread3 t3=new MyThread3();    
    t1.start();    
    t2.start();    
    t3.start();    
  }    
} 

Output:

1
2
3
4
5
10
20
30
40
50
100
200
300
400
500

Conclusion

Using threads in Java will enable flexibility to programmers in their programs. The simplicity of creating, configuring and running threads lets Java programmers devise portable and powerful applications that cannot be made in other third-generation languages. Threads allow any program to perform multiple tasks at once.

97 views

Recent Posts

See All
bottom of page