Close view of multithreading in java

Posted By : Amit Mishra | 10-May-2021

Introduction

Multithreading is a concept of executing multiple tasks at a time to utilize all the available resources of your CPU which helps you to achieve the execution of tasks independently and reduce the execution time. In java, you can achieve parallelism to a great extend. Multiprocessing is used rather than Multithreading because threads use a shared memory area. They do not require a separate allocation memory area and so they save memory, and context-switching between the threads, thereby taking less time than processing.

Thread Creation

There are three officially two ways to create a thread in java. 

1. Extend your class to java.lang.Thread class

2. Implement Runnable interface in your java class.

As you might be aware that threads are independent of anything in the ecosystem. It doesn't need to be dependent on another process or task to start, this is just to keep thread processing and a normal process processing separate because their interaction might cause trouble. Although, there are a lot of things in java.util.concurrent package which brought executor framework which gives you control of your threads to a great extent. Since threads don't return anything, they are just meant to execute a process, sometimes you may expect a thread to give you some output data that you want to collect, there comes the concept of creating Threads with Callable interface. It has the same property almost that you see in Runnable interface but the difference between them is, Callable#call can return a value whereas Runnable#run can't.

The signatures of both methods are given.

Callable Interface

V call() throws Exception

Runnable Interface

void run()

Creating thread with extending your class with Thread class.

 public class MyClass extends Thread{
    public void run() {
        for(int i=0; i<5; i++) {
            System.out.println("Executing Thread...");
        }
    }
    public static void main(String args[]) {
          MyClass thread = new MyClass();
          thread.start();
    }
}

 

Creating Threads by implementing Runnable interface.

public class MyClass implements Runnable {
    @Override
    public void run() {
        for(int i=0; i<5; i++) {
            System.out.println("Executing Thread...");
        }
    }
    public static void main(String args[]) {
          MyClass mythread = new MyClass();
          Thread thread = new Thread(mythread);
          thread.start();
    }
}

Thread Creation Behind the Scenes.

In both cases, the method start ultimately calls the run method of runnable interface. Behind the scenes the JVM itself has the mapping with System Native thread schedulers and as we all know that in Java all the thread share same stack memory location managed by JVM. Everytime you create a new thread JVM maps that thread to OS native thread for execution and JVM takes care of scheduling.

Thread Interference

Let's understand this with the help of an example.

class Counter {
    int a = 0;
    
    public void increament() {
        a++;
    }
    
    public void decreament() {
        a--;
    }
    
    public int get() {
        return this.a;
    }
}

public static void main(String...args) {
        Counter counter = new Counter();
        Thread thread1 = new Thread(() -> {
            counter.increament();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(counter.get() + " : Thread 1 :");
        });
        
        Thread thread2 = new Thread(() -> {
            counter.decreament();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            counter.increament();
            System.out.println("Should print same result : Thread 2 : " + counter.get());
        });
        
        thread1.start();
        thread2.start();
        
    }

Result

0 : Thread 1 :
Should print same result : Thread 2 : 1

 

Expected Result was 1 and 1 but we have got 0 and 1. What happened behind the scene Thread 1 has increamented the value but then at same moment thread 2 has has decremented it. Hence Thread 1 actually got 0 and Thread 2 again has increamented it, hence thread 2 got 1. This is called thread interference. In order to avoid this we can use a concept called synchronization which blocks the other thread until on thread is working on the object.

Synchronization 

To avoid thread interference we can just add a keyword in the method to allow only one thread to work at a time called synchronization. I.e.

class Counter {
    int a = 0;
    
    public synchronized void increament() {
        a++;
    }
    
    public synchronized void decreament() {
        a--;
    }
    
    public synchronized int get() {
        return this.a;
    }
}

This will resolve issues with thread interfearence.

Instrinsic Lock or Synchronized Block

The concept of synchronized block or intrinsic lock tells that the body of the method should be synchronized so that only one thread can execute it at a time to avoid thread interference or locks.

public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

This blocks other thread if any thread is currently operating on this block.

This was it about what I wanted to talk about though there are a lot more things in multithreading to look for, you can browse the documentation for more details or you can find a lot more online resources to learn more.

About Author

Author Image
Amit Mishra

Amit is a spring web developer. He has good knowledge of Spring Cloud, Spring Boot, Spring MVC, Hibernate, and some template engines like jsp and thymleaf.

Request for Proposal

Name is required

Comment is required

Sending message..